File indexing completed on 2024-04-28 15:27:49

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