File indexing completed on 2024-04-21 03:56:05

0001 /*
0002     This file is part of the test suite of the Qt Toolkit.
0003 
0004     SPDX-FileCopyrightText: 2013 Digia Plc and/or its subsidiary(-ies) <https://www.qt.io/terms-conditions/>
0005     SPDX-FileCopyrightText: 2010 Stephen Kelly <steveire@gmail.com>
0006 
0007     SPDX-License-Identifier: LGPL-2.1-only
0008 */
0009 
0010 #include "modeltest.h"
0011 
0012 #include <QTest>
0013 #include <qabstracteventdispatcher.h>
0014 
0015 /*!
0016     Connect to all of the models signals.  Whenever anything happens recheck everything.
0017 */
0018 ModelTest::ModelTest(QAbstractItemModel *_model, Mode testType, QObject *parent)
0019     : QObject(parent)
0020     , model(_model)
0021     , fetchingMore(false)
0022     , pedantic(testType == Pedantic)
0023 {
0024     init();
0025     if (pedantic) {
0026         refreshStatus();
0027         // This is almost certainly not needed.
0028         //         connect(QAbstractEventDispatcher::instance(), &QAbstractEventDispatcher::awake, this, &ModelTest::ensureSteady);
0029     }
0030 }
0031 
0032 ModelTest::ModelTest(QAbstractItemModel *_model, QObject *parent)
0033     : QObject(parent)
0034     , model(_model)
0035     , fetchingMore(false)
0036     , pedantic(false)
0037 {
0038     init();
0039 }
0040 
0041 void ModelTest::init()
0042 {
0043     if (!model) {
0044         qFatal("%s: model must not be null", Q_FUNC_INFO);
0045     }
0046 
0047     connect(model, &QAbstractItemModel::columnsAboutToBeInserted, this, &ModelTest::runAllTests);
0048     connect(model, &QAbstractItemModel::columnsAboutToBeRemoved, this, &ModelTest::runAllTests);
0049     connect(model, &QAbstractItemModel::columnsInserted, this, &ModelTest::runAllTests);
0050     connect(model, &QAbstractItemModel::columnsRemoved, this, &ModelTest::runAllTests);
0051     connect(model, &QAbstractItemModel::dataChanged, this, &ModelTest::runAllTests);
0052     connect(model, &QAbstractItemModel::headerDataChanged, this, &ModelTest::runAllTests);
0053     connect(model, &QAbstractItemModel::layoutAboutToBeChanged, this, &ModelTest::runAllTests);
0054     connect(model, &QAbstractItemModel::layoutChanged, this, &ModelTest::runAllTests);
0055     connect(model, &QAbstractItemModel::modelReset, this, &ModelTest::runAllTests);
0056     connect(model, &QAbstractItemModel::modelAboutToBeReset, this, &ModelTest::modelAboutToBeReset);
0057     connect(model, &QAbstractItemModel::modelReset, this, &ModelTest::modelReset);
0058     connect(model, &QAbstractItemModel::rowsAboutToBeInserted, this, &ModelTest::runAllTests);
0059     connect(model, &QAbstractItemModel::rowsAboutToBeRemoved, this, &ModelTest::runAllTests);
0060     connect(model, &QAbstractItemModel::rowsInserted, this, &ModelTest::runAllTests);
0061     connect(model, &QAbstractItemModel::rowsRemoved, this, &ModelTest::runAllTests);
0062 
0063     // Special checks for changes
0064     connect(model, &QAbstractItemModel::layoutAboutToBeChanged, this, &ModelTest::layoutAboutToBeChanged);
0065     connect(model, &QAbstractItemModel::layoutChanged, this, &ModelTest::layoutChanged);
0066 
0067     connect(model, &QAbstractItemModel::rowsAboutToBeInserted, this, &ModelTest::rowsAboutToBeInserted);
0068     connect(model, &QAbstractItemModel::rowsAboutToBeRemoved, this, &ModelTest::rowsAboutToBeRemoved);
0069     connect(model, &QAbstractItemModel::rowsInserted, this, &ModelTest::rowsInserted);
0070     connect(model, &QAbstractItemModel::rowsRemoved, this, &ModelTest::rowsRemoved);
0071     connect(model, &QAbstractItemModel::rowsAboutToBeMoved, this, &ModelTest::rowsAboutToBeMoved);
0072     connect(model, &QAbstractItemModel::rowsMoved, this, &ModelTest::rowsMoved);
0073     connect(model, &QAbstractItemModel::dataChanged, this, &ModelTest::dataChanged);
0074     connect(model, &QAbstractItemModel::headerDataChanged, this, &ModelTest::headerDataChanged);
0075 
0076     runAllTests();
0077 }
0078 
0079 void ModelTest::runAllTests()
0080 {
0081     if (fetchingMore) {
0082         return;
0083     }
0084     nonDestructiveBasicTest();
0085     rowCount();
0086     columnCount();
0087     hasIndex();
0088     index();
0089     parent();
0090     data();
0091 }
0092 
0093 /*!
0094     nonDestructiveBasicTest tries to call a number of the basic functions (not all)
0095     to make sure the model doesn't outright segfault, testing the functions that makes sense.
0096 */
0097 void ModelTest::nonDestructiveBasicTest()
0098 {
0099     QVERIFY(model->buddy(QModelIndex()) == QModelIndex());
0100     model->canFetchMore(QModelIndex());
0101     QVERIFY(model->columnCount(QModelIndex()) >= 0);
0102     QVERIFY(model->data(QModelIndex()) == QVariant());
0103     fetchingMore = true;
0104     model->fetchMore(QModelIndex());
0105     fetchingMore = false;
0106     Qt::ItemFlags flags = model->flags(QModelIndex());
0107     QVERIFY(flags == Qt::ItemIsDropEnabled || flags == 0);
0108     model->hasChildren(QModelIndex());
0109     model->hasIndex(0, 0);
0110     model->headerData(0, Qt::Horizontal);
0111     model->index(0, 0);
0112     model->itemData(QModelIndex());
0113     QVariant cache;
0114     model->match(QModelIndex(), -1, cache);
0115     model->mimeTypes();
0116     QVERIFY(model->parent(QModelIndex()) == QModelIndex());
0117     QVERIFY(model->rowCount() >= 0);
0118     QVariant variant;
0119     model->setData(QModelIndex(), variant, -1);
0120     model->setHeaderData(-1, Qt::Horizontal, QVariant());
0121     model->setHeaderData(999999, Qt::Horizontal, QVariant());
0122     QMap<int, QVariant> roles;
0123     model->sibling(0, 0, QModelIndex());
0124     model->span(QModelIndex());
0125     model->supportedDropActions();
0126 }
0127 
0128 /*!
0129     Tests model's implementation of QAbstractItemModel::rowCount() and hasChildren()
0130 
0131     Models that are dynamically populated are not as fully tested here.
0132  */
0133 void ModelTest::rowCount()
0134 {
0135     //     qDebug() << "rc";
0136     // check top row
0137     QModelIndex topIndex = model->index(0, 0, QModelIndex());
0138     int rows = model->rowCount(topIndex);
0139     QVERIFY(rows >= 0);
0140     QCOMPARE(model->hasChildren(topIndex), rows > 0);
0141 
0142     QModelIndex secondLevelIndex = model->index(0, 0, topIndex);
0143     if (secondLevelIndex.isValid()) { // not the top level
0144         // check a row count where parent is valid
0145         rows = model->rowCount(secondLevelIndex);
0146         QVERIFY(rows >= 0);
0147         if (rows > 0) {
0148             QVERIFY(model->hasChildren(secondLevelIndex));
0149         }
0150     }
0151 
0152     // The models rowCount() is tested more extensively in checkChildren(),
0153     // but this catches the big mistakes
0154 }
0155 
0156 /*!
0157     Tests model's implementation of QAbstractItemModel::columnCount() and hasChildren()
0158  */
0159 void ModelTest::columnCount()
0160 {
0161     // check top row
0162     QModelIndex topIndex = model->index(0, 0, QModelIndex());
0163     QVERIFY(model->columnCount(topIndex) >= 0);
0164 
0165     // check a column count where parent is valid
0166     QModelIndex childIndex = model->index(0, 0, topIndex);
0167     if (childIndex.isValid()) {
0168         QVERIFY(model->columnCount(childIndex) >= 0);
0169     }
0170 
0171     // columnCount() is tested more extensively in checkChildren(),
0172     // but this catches the big mistakes
0173 }
0174 
0175 /*!
0176     Tests model's implementation of QAbstractItemModel::hasIndex()
0177  */
0178 void ModelTest::hasIndex()
0179 {
0180     //     qDebug() << "hi";
0181     // Make sure that invalid values returns an invalid index
0182     QVERIFY(!model->hasIndex(-2, -2));
0183     QVERIFY(!model->hasIndex(-2, 0));
0184     QVERIFY(!model->hasIndex(0, -2));
0185 
0186     int rows = model->rowCount();
0187     int columns = model->columnCount();
0188 
0189     // check out of bounds
0190     QVERIFY(!model->hasIndex(rows, columns));
0191     QVERIFY(!model->hasIndex(rows + 1, columns + 1));
0192 
0193     QCOMPARE(model->hasIndex(0, 0), rows > 0);
0194 
0195     // hasIndex() is tested more extensively in checkChildren(),
0196     // but this catches the big mistakes
0197 }
0198 
0199 /*!
0200     Tests model's implementation of QAbstractItemModel::index()
0201  */
0202 void ModelTest::index()
0203 {
0204     //     qDebug() << "i";
0205     // Make sure that invalid values returns an invalid index
0206     QVERIFY(model->index(-2, -2) == QModelIndex());
0207     QVERIFY(model->index(-2, 0) == QModelIndex());
0208     QVERIFY(model->index(0, -2) == QModelIndex());
0209 
0210     int rows = model->rowCount();
0211     int columns = model->columnCount();
0212 
0213     if (rows == 0) {
0214         return;
0215     }
0216 
0217     // Catch off by one errors
0218     QVERIFY(model->index(rows, columns) == QModelIndex());
0219     QVERIFY(model->index(0, 0).isValid());
0220 
0221     // Make sure that the same index is *always* returned
0222     QModelIndex a = model->index(0, 0);
0223     QModelIndex b = model->index(0, 0);
0224     QVERIFY(a == b);
0225 
0226     // index() is tested more extensively in checkChildren(),
0227     // but this catches the big mistakes
0228 }
0229 
0230 /*!
0231     Tests model's implementation of QAbstractItemModel::parent()
0232  */
0233 void ModelTest::parent()
0234 {
0235     //     qDebug() << "p";
0236     // Make sure the model won't crash and will return an invalid QModelIndex
0237     // when asked for the parent of an invalid index.
0238     QVERIFY(model->parent(QModelIndex()) == QModelIndex());
0239 
0240     if (model->rowCount() == 0) {
0241         return;
0242     }
0243 
0244     // Column 0                | Column 1    |
0245     // QModelIndex()           |             |
0246     //    \- topIndex          | topIndex1   |
0247     //         \- childIndex   | childIndex1 |
0248 
0249     // Common error test #1, make sure that a top level index has a parent
0250     // that is a invalid QModelIndex.
0251     QModelIndex topIndex = model->index(0, 0, QModelIndex());
0252     QVERIFY(model->parent(topIndex) == QModelIndex());
0253 
0254     // Common error test #2, make sure that a second level index has a parent
0255     // that is the first level index.
0256     if (model->rowCount(topIndex) > 0) {
0257         QModelIndex childIndex = model->index(0, 0, topIndex);
0258         if (model->parent(childIndex) != topIndex) {
0259             qDebug() << model->parent(childIndex) << topIndex << topIndex.data();
0260         }
0261         QVERIFY(model->parent(childIndex) == topIndex);
0262     }
0263 
0264     // Common error test #3, the second column should NOT have the same children
0265     // as the first column in a row.
0266     // Usually the second column shouldn't have children.
0267     QModelIndex topIndex1 = model->index(0, 1, QModelIndex());
0268     if (model->rowCount(topIndex1) > 0) {
0269         QModelIndex childIndex = model->index(0, 0, topIndex);
0270         QModelIndex childIndex1 = model->index(0, 0, topIndex1);
0271         QVERIFY(childIndex != childIndex1);
0272     }
0273 
0274     // Full test, walk n levels deep through the model making sure that all
0275     // parent's children correctly specify their parent.
0276     checkChildren(QModelIndex());
0277 }
0278 
0279 /*!
0280     Called from the parent() test.
0281 
0282     A model that returns an index of parent X should also return X when asking
0283     for the parent of the index.
0284 
0285     This recursive function does pretty extensive testing on the whole model in an
0286     effort to catch edge cases.
0287 
0288     This function assumes that rowCount(), columnCount() and index() already work.
0289     If they have a bug it will point it out, but the above tests should have already
0290     found the basic bugs because it is easier to figure out the problem in
0291     those tests then this one.
0292  */
0293 void ModelTest::checkChildren(const QModelIndex &parent, int currentDepth)
0294 {
0295     // First just try walking back up the tree.
0296     QModelIndex p = parent;
0297     while (p.isValid()) {
0298         p = p.parent();
0299     }
0300 
0301     // For models that are dynamically populated
0302     if (model->canFetchMore(parent)) {
0303         fetchingMore = true;
0304         model->fetchMore(parent);
0305         fetchingMore = false;
0306     }
0307 
0308     int rows = model->rowCount(parent);
0309     int columns = model->columnCount(parent);
0310 
0311     // Some further testing against rows(), columns(), and hasChildren()
0312     QVERIFY(rows >= 0);
0313     QVERIFY(columns >= 0);
0314     QCOMPARE(model->hasChildren(parent), rows > 0);
0315 
0316     qDebug() << "parent:" << model->data(parent).toString() << "rows:" << rows << "columns:" << columns << "parent column:" << parent.column();
0317 
0318     const QModelIndex topLeftChild = model->index(0, 0, parent);
0319 
0320     QVERIFY(!model->hasIndex(rows, 0, parent));
0321     QVERIFY(!model->index(rows, 0, parent).isValid());
0322     for (int r = 0; r < rows; ++r) {
0323         if (model->canFetchMore(parent)) {
0324             fetchingMore = true;
0325             model->fetchMore(parent);
0326             fetchingMore = false;
0327         }
0328         QVERIFY(!model->hasIndex(r, columns, parent));
0329         QVERIFY(!model->index(r, columns, parent).isValid());
0330         for (int c = 0; c < columns; ++c) {
0331             QVERIFY(model->hasIndex(r, c, parent));
0332             QModelIndex index = model->index(r, c, parent);
0333             // rowCount() and columnCount() said that it existed...
0334             QVERIFY(index.isValid());
0335 
0336             // index() should always return the same index when called twice in a row
0337             QModelIndex modifiedIndex = model->index(r, c, parent);
0338             QVERIFY(index == modifiedIndex);
0339 
0340             // Make sure we get the same index if we request it twice in a row
0341             QModelIndex a = model->index(r, c, parent);
0342             QModelIndex b = model->index(r, c, parent);
0343             QVERIFY(a == b);
0344 
0345             {
0346                 const QModelIndex sibling = model->sibling(r, c, topLeftChild);
0347                 QVERIFY(index == sibling);
0348             }
0349             {
0350                 const QModelIndex sibling = topLeftChild.sibling(r, c);
0351                 QVERIFY(index == sibling);
0352             }
0353 
0354             // Some basic checking on the index that is returned
0355             QVERIFY(index.model() == model);
0356             QCOMPARE(index.row(), r);
0357             QCOMPARE(index.column(), c);
0358             // While you can technically return a QVariant usually this is a sign
0359             // of a bug in data().  Disable if this really is ok in your model.
0360             if (!model->data(index, Qt::DisplayRole).isValid()) {
0361                 qDebug() << index << index.data() << index.parent();
0362             }
0363             QVERIFY(model->data(index, Qt::DisplayRole).isValid());
0364 
0365             // If the next test fails here is some somewhat useful debug you play with.
0366 
0367             /*
0368             if (model->parent(index) != parent) {
0369                 qDebug() << r << c << currentDepth << model->data(index).toString()
0370                          << model->data(parent).toString();
0371                 qDebug() << index << parent << model->parent(index);
0372 //                 And a view that you can even use to show the model.
0373 //                 QTreeView view;
0374 //                 view.setModel(model);
0375 //                 view.show();
0376             }*/
0377 
0378             // Check that we can get back our real parent.
0379             QCOMPARE(model->parent(index), parent);
0380 
0381             // recursively go down the children
0382             if (model->hasChildren(index) && currentDepth < 10) {
0383                 // qDebug() << r << c << "has children" << model->rowCount(index);
0384                 checkChildren(index, ++currentDepth);
0385             } /* else { if (currentDepth >= 10) qDebug() << "checked 10 deep"; };*/
0386 
0387             // make sure that after testing the children that the index doesn't change.
0388             QModelIndex newerIndex = model->index(r, c, parent);
0389             QVERIFY(index == newerIndex);
0390         }
0391     }
0392 }
0393 
0394 /*!
0395     Tests model's implementation of QAbstractItemModel::data()
0396  */
0397 void ModelTest::data()
0398 {
0399     // Invalid index should return an invalid qvariant
0400     QVERIFY(!model->data(QModelIndex()).isValid());
0401 
0402     if (model->rowCount() == 0) {
0403         return;
0404     }
0405 
0406     // A valid index should have a valid QVariant data
0407     QVERIFY(model->index(0, 0).isValid());
0408 
0409     // shouldn't be able to set data on an invalid index
0410     QVERIFY(!model->setData(QModelIndex(), QLatin1String("foo"), Qt::DisplayRole));
0411 
0412     // General Purpose roles that should return a QString
0413     QVariant variant = model->data(model->index(0, 0), Qt::ToolTipRole);
0414     if (variant.isValid()) {
0415         QVERIFY(variant.canConvert<QString>());
0416     }
0417     variant = model->data(model->index(0, 0), Qt::StatusTipRole);
0418     if (variant.isValid()) {
0419         QVERIFY(variant.canConvert<QString>());
0420     }
0421     variant = model->data(model->index(0, 0), Qt::WhatsThisRole);
0422     if (variant.isValid()) {
0423         QVERIFY(variant.canConvert<QString>());
0424     }
0425 
0426     // General Purpose roles that should return a QSize
0427     variant = model->data(model->index(0, 0), Qt::SizeHintRole);
0428     if (variant.isValid()) {
0429         QVERIFY(variant.canConvert<QSize>());
0430     }
0431 
0432     // General Purpose roles that should return a QFont
0433     QVariant fontVariant = model->data(model->index(0, 0), Qt::FontRole);
0434     if (fontVariant.isValid()) {
0435         QVERIFY(fontVariant.canConvert<QFont>());
0436     }
0437 
0438     // Check that the alignment is one we know about
0439     QVariant textAlignmentVariant = model->data(model->index(0, 0), Qt::TextAlignmentRole);
0440     if (textAlignmentVariant.isValid()) {
0441         const auto alignment = textAlignmentVariant.toUInt();
0442         QCOMPARE(alignment, (alignment & (Qt::AlignHorizontal_Mask | Qt::AlignVertical_Mask)));
0443     }
0444 
0445     // General Purpose roles that should return a QColor
0446     QVariant colorVariant = model->data(model->index(0, 0), Qt::BackgroundRole);
0447     if (colorVariant.isValid()) {
0448         QVERIFY(colorVariant.canConvert<QColor>());
0449     }
0450 
0451     colorVariant = model->data(model->index(0, 0), Qt::ForegroundRole);
0452     if (colorVariant.isValid()) {
0453         QVERIFY(colorVariant.canConvert<QColor>());
0454     }
0455 
0456     // Check that the "check state" is one we know about.
0457     QVariant checkStateVariant = model->data(model->index(0, 0), Qt::CheckStateRole);
0458     if (checkStateVariant.isValid()) {
0459         int state = checkStateVariant.toInt();
0460         QVERIFY(state == Qt::Unchecked || state == Qt::PartiallyChecked || state == Qt::Checked);
0461     }
0462 }
0463 
0464 /*!
0465     Store what is about to be inserted to make sure it actually happens
0466 
0467     \sa rowsInserted()
0468  */
0469 void ModelTest::rowsAboutToBeInserted(const QModelIndex &parent, int start, int end)
0470 {
0471     //     Q_UNUSED(end);
0472     qDebug() << "rowsAboutToBeInserted"
0473              << "start=" << start << "end=" << end << "parent=" << model->data(parent).toString()
0474              << "current count of parent=" << model->rowCount(parent); // << "display of last=" << model->data( model->index(start-1, 0, parent) );
0475     //     qDebug() << model->index(start-1, 0, parent) << model->data( model->index(start-1, 0, parent) );
0476     Changing c;
0477     c.parent = parent;
0478     c.oldSize = model->rowCount(parent);
0479     c.last = model->data(model->index(start - 1, 0, parent));
0480     c.next = model->data(model->index(start, 0, parent));
0481     insert.push(c);
0482     if (pedantic) {
0483         ensureConsistent();
0484         status.type = Status::InsertingRows;
0485     }
0486 }
0487 
0488 /*!
0489     Confirm that what was said was going to happen actually did
0490 
0491     \sa rowsAboutToBeInserted()
0492  */
0493 void ModelTest::rowsInserted(const QModelIndex &parent, int start, int end)
0494 {
0495     Changing c = insert.pop();
0496     QVERIFY(c.parent == parent);
0497     qDebug() << "rowsInserted"
0498              << "start=" << start << "end=" << end << "oldsize=" << c.oldSize << "parent=" << model->data(parent).toString()
0499              << "current rowcount of parent=" << model->rowCount(parent);
0500 
0501     for (int ii = start; ii <= end; ii++) {
0502         qDebug() << "itemWasInserted:" << ii << model->data(model->index(ii, 0, parent));
0503     }
0504 
0505     QVERIFY(c.oldSize + (end - start + 1) == model->rowCount(parent));
0506     QVERIFY(c.last == model->data(model->index(start - 1, 0, c.parent)));
0507 
0508     /*
0509     if (c.next != model->data(model->index(end + 1, 0, c.parent))) {
0510         qDebug() << start << end;
0511         for (int i=0; i < model->rowCount(); ++i)
0512             qDebug() << model->index(i, 0).data().toString();
0513         qDebug() << c.next << model->data(model->index(end + 1, 0, c.parent));
0514     }
0515     */
0516 
0517     QVERIFY(c.next == model->data(model->index(end + 1, 0, c.parent)));
0518 
0519     if (pedantic) {
0520         QVERIFY(status.type == Status::InsertingRows);
0521         refreshStatus();
0522     }
0523 }
0524 
0525 void ModelTest::modelAboutToBeReset()
0526 {
0527     qDebug() << "@@@@@@@@@@@"
0528              << "modelAboutToBeReset";
0529 
0530     if (pedantic) {
0531         ensureConsistent();
0532         status.type = Status::Resetting;
0533     }
0534 }
0535 
0536 void ModelTest::modelReset()
0537 {
0538     qDebug() << "@@@@@@@@@@@"
0539              << "modelReset";
0540     if (pedantic) {
0541         Q_ASSERT(status.type == Status::Resetting);
0542         refreshStatus();
0543     }
0544 }
0545 
0546 void ModelTest::layoutAboutToBeChanged()
0547 {
0548     qDebug() << "@@@@@@@@@@@"
0549              << "layoutAboutToBeChanged";
0550 
0551     if (pedantic) {
0552         ensureConsistent();
0553         status.type = Status::ChangingLayout;
0554     }
0555     for (int i = 0; i < qBound(0, model->rowCount(), 100); ++i) {
0556         changing.append(QPersistentModelIndex(model->index(i, 0)));
0557     }
0558 }
0559 
0560 void ModelTest::layoutChanged()
0561 {
0562     qDebug() << "@@@@@@@@@@@"
0563              << "layoutAboutToBeChanged";
0564     for (int i = 0; i < changing.count(); ++i) {
0565         QPersistentModelIndex p = changing[i];
0566         QVERIFY(p == model->index(p.row(), p.column(), p.parent()));
0567     }
0568     changing.clear();
0569 
0570     if (pedantic) {
0571         QVERIFY(status.type == Status::ChangingLayout);
0572         refreshStatus();
0573     }
0574 }
0575 
0576 void ModelTest::rowsAboutToBeMoved(const QModelIndex &srcParent, int start, int end, const QModelIndex &destParent, int destinationRow)
0577 {
0578     qDebug() << "rowsAboutToBeMoved" << srcParent << start << end << destParent << destinationRow;
0579 
0580     for (int row = start, dr = destinationRow; row <= end; ++row, ++dr) {
0581         qDebug() << "row" << model->index(row, 0, srcParent).data() << "in " << srcParent << "will be moved to " << destParent << dr;
0582     }
0583 
0584     Changing cs;
0585     cs.parent = srcParent;
0586     cs.oldSize = model->rowCount(srcParent);
0587     cs.last = model->data(model->index(start - 1, 0, srcParent));
0588     cs.next = model->data(model->index(end + 1, 0, srcParent));
0589     remove.push(cs);
0590     Changing cd;
0591     cd.parent = destParent;
0592     cd.oldSize = model->rowCount(destParent);
0593     cd.last = model->data(model->index(destinationRow - 1, 0, destParent));
0594     cd.next = model->data(model->index(destinationRow, 0, destParent));
0595     insert.push(cd);
0596 }
0597 
0598 void ModelTest::rowsMoved(const QModelIndex &srcParent, int start, int end, const QModelIndex &destParent, int destinationRow)
0599 {
0600     qDebug() << "rowsMoved" << srcParent << start << end << destParent << destinationRow;
0601 
0602     Changing cd = insert.pop();
0603     QVERIFY(cd.parent == destParent);
0604     if (srcParent == destParent) {
0605         QVERIFY(cd.oldSize == model->rowCount(destParent));
0606 
0607         // TODO: Find out what I can assert here about last and next.
0608         //     QVERIFY ( cd.last == model->data ( model->index ( destinationRow - 1, 0, cd.parent ) ) );
0609         //     QVERIFY ( cd.next == model->data ( model->index ( destinationRow + (end - start + 1), 0, cd.parent ) ) );
0610 
0611     } else {
0612         qDebug() << cd.oldSize << end << start << model->rowCount(destParent) << destParent.data() << "#########";
0613         QVERIFY(cd.oldSize + (end - start + 1) == model->rowCount(destParent));
0614 
0615         QVERIFY(cd.last == model->data(model->index(destinationRow - 1, 0, cd.parent)));
0616         QVERIFY(cd.next == model->data(model->index(destinationRow + (end - start + 1), 0, cd.parent)));
0617     }
0618     Changing cs = remove.pop();
0619 
0620     QVERIFY(cs.parent == srcParent);
0621     if (srcParent == destParent) {
0622         QVERIFY(cs.oldSize == model->rowCount(srcParent));
0623     } else {
0624         QVERIFY(cs.oldSize - (end - start + 1) == model->rowCount(srcParent));
0625 
0626         QVERIFY(cs.last == model->data(model->index(start - 1, 0, srcParent)));
0627         //     qDebug() << cs.next << model->data ( model->index ( start, 0, srcParent ) );
0628         QVERIFY(cs.next == model->data(model->index(start, 0, srcParent)));
0629     }
0630 }
0631 
0632 /*!
0633     Store what is about to be inserted to make sure it actually happens
0634 
0635     \sa rowsRemoved()
0636  */
0637 void ModelTest::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
0638 {
0639     qDebug() << "ratbr" << parent << start << end;
0640     for (int ii = start; ii <= end; ii++) {
0641         qDebug() << "itemwillbe removed:" << model->data(model->index(ii, 0, parent));
0642     }
0643 
0644     if (pedantic) {
0645         ensureConsistent();
0646         status.type = Status::RemovingRows;
0647     }
0648 
0649     Changing c;
0650     c.parent = parent;
0651     c.oldSize = model->rowCount(parent);
0652     c.last = model->data(model->index(start - 1, 0, parent));
0653     c.next = model->data(model->index(end + 1, 0, parent));
0654     remove.push(c);
0655 }
0656 
0657 /*!
0658     Confirm that what was said was going to happen actually did
0659 
0660     \sa rowsAboutToBeRemoved()
0661  */
0662 void ModelTest::rowsRemoved(const QModelIndex &parent, int start, int end)
0663 {
0664     qDebug() << "rr" << parent << start << end;
0665     Changing c = remove.pop();
0666     QVERIFY(c.parent == parent);
0667     qDebug() << (c.oldSize - (end - start + 1)) << model->rowCount(parent);
0668     QVERIFY(c.oldSize - (end - start + 1) == model->rowCount(parent));
0669     QVERIFY(c.last == model->data(model->index(start - 1, 0, c.parent)));
0670     QVERIFY(c.next == model->data(model->index(start, 0, c.parent)));
0671 
0672     if (pedantic) {
0673         Q_ASSERT(status.type == Status::RemovingRows);
0674         refreshStatus();
0675     }
0676 }
0677 
0678 void ModelTest::refreshStatus()
0679 {
0680     status.type = Status::Idle;
0681     status.nonPersistent.clear();
0682     status.persistent.clear();
0683 
0684     persistStatus(QModelIndex());
0685 }
0686 
0687 void ModelTest::persistStatus(const QModelIndex &index)
0688 {
0689     const int rowCount = model->rowCount(index);
0690     for (int row = 0; row < rowCount; ++row) {
0691         // TODO: Test multi columns
0692         static const int column = 0;
0693         QPersistentModelIndex idx = model->index(row, column, index);
0694         status.persistent.append(idx);
0695         status.nonPersistent.append(idx);
0696         persistStatus(idx);
0697     }
0698 }
0699 
0700 void ModelTest::ensureSteady()
0701 {
0702     Q_ASSERT(insert.isEmpty());
0703     Q_ASSERT(remove.isEmpty());
0704     Q_ASSERT(changing.isEmpty());
0705     ensureConsistent();
0706 }
0707 
0708 void ModelTest::ensureConsistent()
0709 {
0710     Q_ASSERT(status.type == Status::Idle);
0711 
0712     Q_ASSERT(status.nonPersistent.size() == status.persistent.size());
0713     for (int i = 0; i < status.nonPersistent.size(); ++i) {
0714         Q_ASSERT(status.nonPersistent.at(i) == status.persistent.at(i));
0715     }
0716 }
0717 
0718 void ModelTest::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
0719 {
0720     QVERIFY(topLeft.isValid());
0721     QVERIFY(bottomRight.isValid());
0722     QModelIndex commonParent = bottomRight.parent();
0723     QVERIFY(topLeft.parent() == commonParent);
0724     QVERIFY(topLeft.row() <= bottomRight.row());
0725     QVERIFY(topLeft.column() <= bottomRight.column());
0726     int rowCount = model->rowCount(commonParent);
0727     int columnCount = model->columnCount(commonParent);
0728     QVERIFY(bottomRight.row() < rowCount);
0729     QVERIFY(bottomRight.column() < columnCount);
0730 }
0731 
0732 void ModelTest::headerDataChanged(Qt::Orientation orientation, int start, int end)
0733 {
0734     QVERIFY(start >= 0);
0735     QVERIFY(end >= 0);
0736     QVERIFY(start <= end);
0737     int itemCount = orientation == Qt::Vertical ? model->rowCount() : model->columnCount();
0738     QVERIFY(start < itemCount);
0739     QVERIFY(end < itemCount);
0740 }
0741 
0742 #include "moc_modeltest.cpp"