File indexing completed on 2024-03-24 17:25:15

0001 /****************************************************************************
0002 **
0003 ** Copyright (C) 2007 Trolltech ASA. All rights reserved.
0004 **
0005 ** This file is part of the Qt Concurrent project on Trolltech Labs.
0006 **
0007 ** This file may be used under the terms of the GNU General Public
0008 ** License version 2.0 as published by the Free Software Foundation
0009 ** and appearing in the file LICENSE.GPL included in the packaging of
0010 ** this file.  Please review the following information to ensure GNU
0011 ** General Public Licensing requirements will be met:
0012 ** http://www.trolltech.com/products/qt/opensource.html
0013 **
0014 ** If you are unsure which license is appropriate for your use, please
0015 ** review the following information:
0016 ** http://www.trolltech.com/products/qt/licensing.html or contact the
0017 ** sales department at sales@trolltech.com.
0018 **
0019 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
0020 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
0021 **
0022 ****************************************************************************/
0023 
0024 #include "modeltest.h"
0025 
0026 #include <QSize>
0027 
0028 Q_DECLARE_METATYPE(QModelIndex)
0029 
0030 /*!
0031     Connect to all of the models signals.  Whenever anything happens recheck everything.
0032 */
0033 ModelTest::ModelTest(QAbstractItemModel *_model, QObject *parent) : QObject(parent), model(_model), fetchingMore(false)
0034 {
0035     Q_ASSERT(model);
0036 
0037     connect(model, &QAbstractItemModel::columnsAboutToBeInserted,
0038             this, &ModelTest::runAllTests);
0039     connect(model, &QAbstractItemModel::columnsAboutToBeRemoved,
0040             this, &ModelTest::runAllTests);
0041     connect(model, &QAbstractItemModel::columnsInserted,
0042             this, &ModelTest::runAllTests);
0043     connect(model, &QAbstractItemModel::columnsRemoved,
0044             this, &ModelTest::runAllTests);
0045     connect(model, &QAbstractItemModel::dataChanged,
0046             this, &ModelTest::runAllTests);
0047     connect(model, &QAbstractItemModel::headerDataChanged,
0048             this, &ModelTest::runAllTests);
0049     connect(model, &QAbstractItemModel::layoutAboutToBeChanged,
0050             this, &ModelTest::runAllTests);
0051     connect(model, &QAbstractItemModel::layoutChanged,
0052             this, &ModelTest::runAllTests);
0053     connect(model, &QAbstractItemModel::modelReset,
0054             this, &ModelTest::runAllTests);
0055     connect(model, &QAbstractItemModel::rowsAboutToBeInserted,
0056             this, &ModelTest::runAllTests);
0057     connect(model, &QAbstractItemModel::rowsAboutToBeRemoved,
0058             this, &ModelTest::runAllTests);
0059     connect(model, &QAbstractItemModel::rowsInserted,
0060             this, &ModelTest::runAllTests);
0061     connect(model, &QAbstractItemModel::rowsRemoved,
0062             this, &ModelTest::runAllTests);
0063 
0064     // Special checks for inserting/removing
0065     connect(model, &QAbstractItemModel::rowsAboutToBeInserted, this, &ModelTest::rowsAboutToBeInserted);
0066     connect(model, &QAbstractItemModel::rowsAboutToBeRemoved, this, &ModelTest::rowsAboutToBeRemoved);
0067     connect(model, &QAbstractItemModel::rowsInserted, this, &ModelTest::rowsInserted);
0068     connect(model, &QAbstractItemModel::rowsRemoved, this, &ModelTest::rowsRemoved);
0069 
0070     runAllTests();
0071 }
0072 
0073 void ModelTest::runAllTests()
0074 {
0075     if (fetchingMore) {
0076         return;
0077     }
0078     nonDestructiveBasicTest();
0079     rowCount();
0080     columnCount();
0081     hasIndex();
0082     index();
0083     parent();
0084     data();
0085 }
0086 
0087 /*!
0088     nonDestructiveBasicTest tries to call a number of the basic functions (not all)
0089     to make sure the model doesn't outright segfault, testing the functions that makes sense.
0090 */
0091 void ModelTest::nonDestructiveBasicTest()
0092 {
0093     Q_ASSERT(model->buddy(QModelIndex()) == QModelIndex());
0094     model->canFetchMore(QModelIndex());
0095     Q_ASSERT(model->columnCount(QModelIndex()) >= 0);
0096     Q_ASSERT(model->data(QModelIndex()) == QVariant());
0097     fetchingMore = true;
0098     model->fetchMore(QModelIndex());
0099     fetchingMore = false;
0100     Qt::ItemFlags flags = model->flags(QModelIndex());
0101     Q_ASSERT(flags == Qt::ItemIsDropEnabled || flags == 0);
0102     model->hasChildren(QModelIndex());
0103     model->hasIndex(0, 0);
0104     model->headerData(0, Qt::Horizontal);
0105     model->index(0, 0);
0106     model->itemData(QModelIndex());
0107     QVariant cache;
0108     model->match(QModelIndex(), -1, cache);
0109     model->mimeTypes();
0110     Q_ASSERT(model->parent(QModelIndex()) == QModelIndex());
0111     Q_ASSERT(model->rowCount() >= 0);
0112     QVariant variant;
0113     model->setData(QModelIndex(), variant, -1);
0114     model->setHeaderData(-1, Qt::Horizontal, QVariant());
0115     model->setHeaderData(0, Qt::Horizontal, QVariant());
0116     model->setHeaderData(999999, Qt::Horizontal, QVariant());
0117     QMap<int, QVariant> roles;
0118     model->sibling(0, 0, QModelIndex());
0119     model->span(QModelIndex());
0120     model->supportedDropActions();
0121 }
0122 
0123 /*!
0124     Tests model's implementation of QAbstractItemModel::rowCount() and hasChildren()
0125 
0126     Models that are dynamically populated are not as fully tested here.
0127  */
0128 void ModelTest::rowCount()
0129 {
0130     // check top row
0131     QModelIndex topIndex = model->index(0, 0, QModelIndex());
0132     int rows = model->rowCount(topIndex);
0133     Q_ASSERT(rows >= 0);
0134     if (rows > 0) {
0135         Q_ASSERT(model->hasChildren(topIndex) == true);
0136     }
0137 
0138     QModelIndex secondLevelIndex = model->index(0, 0, topIndex);
0139     if (secondLevelIndex.isValid()) { // not the top level
0140         // check a row count where parent is valid
0141         rows = model->rowCount(secondLevelIndex);
0142         Q_ASSERT(rows >= 0);
0143         if (rows > 0) {
0144             Q_ASSERT(model->hasChildren(secondLevelIndex) == true);
0145         }
0146     }
0147 
0148     // The models rowCount() is tested more extensively in checkChildren(),
0149     // but this catches the big mistakes
0150 }
0151 
0152 /*!
0153     Tests model's implementation of QAbstractItemModel::columnCount() and hasChildren()
0154  */
0155 void ModelTest::columnCount()
0156 {
0157     // check top row
0158     QModelIndex topIndex = model->index(0, 0, QModelIndex());
0159     Q_ASSERT(model->columnCount(topIndex) >= 0);
0160 
0161     // check a column count where parent is valid
0162     QModelIndex childIndex = model->index(0, 0, topIndex);
0163     if (childIndex.isValid()) {
0164         Q_ASSERT(model->columnCount(childIndex) >= 0);
0165     }
0166 
0167     // columnCount() is tested more extensively in checkChildren(),
0168     // but this catches the big mistakes
0169 }
0170 
0171 /*!
0172     Tests model's implementation of QAbstractItemModel::hasIndex()
0173  */
0174 void ModelTest::hasIndex()
0175 {
0176     // Make sure that invalid values returns an invalid index
0177     Q_ASSERT(model->hasIndex(-2, -2) == false);
0178     Q_ASSERT(model->hasIndex(-2, 0) == false);
0179     Q_ASSERT(model->hasIndex(0, -2) == false);
0180 
0181     int rows = model->rowCount();
0182     int columns = model->columnCount();
0183 
0184     // check out of bounds
0185     Q_ASSERT(model->hasIndex(rows, columns) == false);
0186     Q_ASSERT(model->hasIndex(rows + 1, columns + 1) == false);
0187 
0188     if (rows > 0) {
0189         Q_ASSERT(model->hasIndex(0, 0) == true);
0190     }
0191 
0192     // hasIndex() is tested more extensively in checkChildren(),
0193     // but this catches the big mistakes
0194 }
0195 
0196 /*!
0197     Tests model's implementation of QAbstractItemModel::index()
0198  */
0199 void ModelTest::index()
0200 {
0201     // Make sure that invalid values returns an invalid index
0202     Q_ASSERT(model->index(-2, -2) == QModelIndex());
0203     Q_ASSERT(model->index(-2, 0) == QModelIndex());
0204     Q_ASSERT(model->index(0, -2) == QModelIndex());
0205 
0206     int rows = model->rowCount();
0207     int columns = model->columnCount();
0208 
0209     if (rows == 0) {
0210         return;
0211     }
0212 
0213     // Catch off by one errors
0214     Q_ASSERT(model->index(rows, columns) == QModelIndex());
0215     Q_ASSERT(model->index(0, 0).isValid() == true);
0216 
0217     // Make sure that the same index is *always* returned
0218     QModelIndex a = model->index(0, 0);
0219     QModelIndex b = model->index(0, 0);
0220     Q_ASSERT(a == b);
0221 
0222     // index() is tested more extensively in checkChildren(),
0223     // but this catches the big mistakes
0224 }
0225 
0226 /*!
0227     Tests model's implementation of QAbstractItemModel::parent()
0228  */
0229 void ModelTest::parent()
0230 {
0231     // Make sure the model wont crash and will return an invalid QModelIndex
0232     // when asked for the parent of an invalid index.
0233     Q_ASSERT(model->parent(QModelIndex()) == QModelIndex());
0234 
0235     if (model->rowCount() == 0) {
0236         return;
0237     }
0238 
0239     // Column 0                | Column 1    |
0240     // QModelIndex()           |             |
0241     //    \- topIndex          | topIndex1   |
0242     //         \- childIndex   | childIndex1 |
0243 
0244     // Common error test #1, make sure that a top level index has a parent
0245     // that is a invalid QModelIndex.
0246     QModelIndex topIndex = model->index(0, 0, QModelIndex());
0247     Q_ASSERT(model->parent(topIndex) == QModelIndex());
0248 
0249     // Common error test #2, make sure that a second level index has a parent
0250     // that is the first level index.
0251     if (model->rowCount(topIndex) > 0) {
0252         QModelIndex childIndex = model->index(0, 0, topIndex);
0253         Q_ASSERT(model->parent(childIndex) == topIndex);
0254     }
0255 
0256     // Common error test #3, the second column should NOT have the same children
0257     // as the first column in a row.
0258     // Usually the second column shouldn't have children.
0259     QModelIndex topIndex1 = model->index(0, 1, QModelIndex());
0260     if (model->rowCount(topIndex1) > 0) {
0261         QModelIndex childIndex = model->index(0, 0, topIndex);
0262         QModelIndex childIndex1 = model->index(0, 0, topIndex1);
0263         Q_ASSERT(childIndex != childIndex1);
0264     }
0265 
0266     // Full test, walk n levels deep through the model making sure that all
0267     // parent's children correctly specify their parent.
0268     checkChildren(QModelIndex());
0269 }
0270 
0271 /*!
0272     Called from the parent() test.
0273 
0274     A model that returns an index of parent X should also return X when asking
0275     for the parent of the index.
0276 
0277     This recursive function does pretty extensive testing on the whole model in an
0278     effort to catch edge cases.
0279 
0280     This function assumes that rowCount(), columnCount() and index() already work.
0281     If they have a bug it will point it out, but the above tests should have already
0282     found the basic bugs because it is easier to figure out the problem in
0283     those tests then this one.
0284  */
0285 void ModelTest::checkChildren(const QModelIndex &parent, int currentDepth)
0286 {
0287     // First just try walking back up the tree.
0288     QModelIndex p = parent;
0289     while (p.isValid()) {
0290         p = p.parent();
0291     }
0292 
0293     // For models that are dynamically populated
0294     if (model->canFetchMore(parent)) {
0295         fetchingMore = true;
0296         model->fetchMore(parent);
0297         fetchingMore = false;
0298     }
0299 
0300     int rows = model->rowCount(parent);
0301     int columns = model->columnCount(parent);
0302 
0303     if (rows > 0) {
0304         Q_ASSERT(model->hasChildren(parent));
0305     }
0306 
0307     // Some further testing against rows(), columns(), and hasChildren()
0308     Q_ASSERT(rows >= 0);
0309     Q_ASSERT(columns >= 0);
0310     if (rows > 0) {
0311         Q_ASSERT(model->hasChildren(parent) == true);
0312     }
0313 
0314     //qDebug() << "parent:" << model->data(parent).toString() << "rows:" << rows
0315     //         << "columns:" << columns << "parent column:" << parent.column();
0316 
0317     Q_ASSERT(model->hasIndex(rows + 1, 0, parent) == false);
0318     for (int r = 0; r < rows; ++r) {
0319         if (model->canFetchMore(parent)) {
0320             fetchingMore = true;
0321             model->fetchMore(parent);
0322             fetchingMore = false;
0323         }
0324         Q_ASSERT(model->hasIndex(r, columns + 1, parent) == false);
0325         for (int c = 0; c < columns; ++c) {
0326             Q_ASSERT(model->hasIndex(r, c, parent) == true);
0327             QModelIndex index = model->index(r, c, parent);
0328             // rowCount() and columnCount() said that it existed...
0329             Q_ASSERT(index.isValid() == true);
0330 
0331             // index() should always return the same index when called twice in a row
0332             QModelIndex modifiedIndex = model->index(r, c, parent);
0333             Q_ASSERT(index == modifiedIndex);
0334 
0335             // Make sure we get the same index if we request it twice in a row
0336             QModelIndex a = model->index(r, c, parent);
0337             QModelIndex b = model->index(r, c, parent);
0338             Q_ASSERT(a == b);
0339 
0340             // Some basic checking on the index that is returned
0341             Q_ASSERT(index.model() == model);
0342             Q_ASSERT(index.row() == r);
0343             Q_ASSERT(index.column() == c);
0344             // While you can technically return a QVariant usually this is a sign
0345             // of an bug in data()  Disable if this really is ok in your model.
0346             Q_ASSERT(model->data(index, Qt::DisplayRole).isValid() == true);
0347 
0348             // If the next test fails here is some somewhat useful debug you play with.
0349             /*
0350             if (model->parent(index) != parent) {
0351                 qDebug() << r << c << currentDepth << model->data(index).toString()
0352                          << model->data(parent).toString();
0353                 qDebug() << index << parent << model->parent(index);
0354                 // And a view that you can even use to show the model.
0355                 //QTreeView view;
0356                 //view.setModel(model);
0357                 //view.show();
0358             }*/
0359 
0360             // Check that we can get back our real parent.
0361             Q_ASSERT(model->parent(index) == parent);
0362 
0363             // recursively go down the children
0364             if (model->hasChildren(index) && currentDepth < 10) {
0365                 //qDebug() << r << c << "has children" << model->rowCount(index);
0366                 checkChildren(index, ++currentDepth);
0367             }/* else { if (currentDepth >= 10) qDebug() << "checked 10 deep"; };*/
0368 
0369             // make sure that after testing the children that the index doesn't change.
0370             QModelIndex newerIndex = model->index(r, c, parent);
0371             Q_ASSERT(index == newerIndex);
0372         }
0373     }
0374 }
0375 
0376 /*!
0377     Tests model's implementation of QAbstractItemModel::data()
0378  */
0379 void ModelTest::data()
0380 {
0381     // Invalid index should return an invalid qvariant
0382     Q_ASSERT(!model->data(QModelIndex()).isValid());
0383 
0384     if (model->rowCount() == 0) {
0385         return;
0386     }
0387 
0388     // A valid index should have a valid QVariant data
0389     Q_ASSERT(model->index(0, 0).isValid());
0390 
0391     // shouldn't be able to set data on an invalid index
0392     Q_ASSERT(model->setData(QModelIndex(), "foo", Qt::DisplayRole) == false);
0393 
0394     // General Purpose roles that should return a QString
0395     QVariant variant = model->data(model->index(0, 0), Qt::ToolTipRole);
0396     if (variant.isValid()) {
0397         Q_ASSERT(variant.canConvert<QString>());
0398     }
0399     variant = model->data(model->index(0, 0), Qt::StatusTipRole);
0400     if (variant.isValid()) {
0401         Q_ASSERT(variant.canConvert<QString>());
0402     }
0403     variant = model->data(model->index(0, 0), Qt::WhatsThisRole);
0404     if (variant.isValid()) {
0405         Q_ASSERT(variant.canConvert<QString>());
0406     }
0407 
0408     // General Purpose roles that should return a QSize
0409     variant = model->data(model->index(0, 0), Qt::SizeHintRole);
0410     if (variant.isValid()) {
0411         Q_ASSERT(variant.canConvert<QSize>());
0412     }
0413 
0414     // General Purpose roles that should return a QFont
0415     QVariant fontVariant = model->data(model->index(0, 0), Qt::FontRole);
0416     if (fontVariant.isValid()) {
0417         Q_ASSERT(variant.canConvert<QFont>());
0418     }
0419 
0420     // Check that the alignment is one we know about
0421     QVariant textAlignmentVariant = model->data(model->index(0, 0), Qt::TextAlignmentRole);
0422     if (textAlignmentVariant.isValid()) {
0423         int alignment = textAlignmentVariant.toInt();
0424         Q_ASSERT(alignment == Qt::AlignLeft ||
0425                  alignment == Qt::AlignRight ||
0426                  alignment == Qt::AlignHCenter ||
0427                  alignment == Qt::AlignJustify);
0428     }
0429 
0430     // General Purpose roles that should return a QColor
0431     QVariant colorVariant = model->data(model->index(0, 0), Qt::BackgroundColorRole);
0432     if (colorVariant.isValid()) {
0433         Q_ASSERT(variant.canConvert<QColor>());
0434     }
0435 
0436     colorVariant = model->data(model->index(0, 0), Qt::TextColorRole);
0437     if (colorVariant.isValid()) {
0438         Q_ASSERT(variant.canConvert<QColor>());
0439     }
0440 
0441     // Check that the "check state" is one we know about.
0442     QVariant checkStateVariant = model->data(model->index(0, 0), Qt::CheckStateRole);
0443     if (checkStateVariant.isValid()) {
0444         int state = checkStateVariant.toInt();
0445         Q_ASSERT(state == Qt::Unchecked ||
0446                  state == Qt::PartiallyChecked ||
0447                  state == Qt::Checked);
0448     }
0449 }
0450 
0451 /*!
0452     Store what is about to be inserted to make sure it actually happens
0453 
0454     \sa rowsInserted()
0455  */
0456 void ModelTest::rowsAboutToBeInserted(const QModelIndex &parent, int start, int end)
0457 {
0458     Q_UNUSED(end);
0459     Changing c;
0460     c.parent = parent;
0461     c.oldSize = model->rowCount(parent);
0462     c.last = model->data(model->index(start - 1, 0, parent));
0463     c.next = model->data(model->index(start, 0, parent));
0464     insert.push(c);
0465 }
0466 
0467 /*!
0468     Confirm that what was said was going to happen actually did
0469 
0470     \sa rowsAboutToBeInserted()
0471  */
0472 void ModelTest::rowsInserted(const QModelIndex &parent, int start, int end)
0473 {
0474     Changing c = insert.pop();
0475     Q_ASSERT(c.parent == parent);
0476     Q_ASSERT(c.oldSize + (end - start + 1) == model->rowCount(parent));
0477     Q_ASSERT(c.last == model->data(model->index(start - 1, 0, c.parent)));
0478     /*
0479     if (c.next != model->data(model->index(end + 1, 0, c.parent))) {
0480         qDebug() << start << end;
0481         for (int i=0; i < model->rowCount(); ++i)
0482             qDebug() << model->index(i, 0).data().toString();
0483         qDebug() << c.next << model->data(model->index(end + 1, 0, c.parent));
0484     }
0485     */
0486     Q_ASSERT(c.next == model->data(model->index(end + 1, 0, c.parent)));
0487 }
0488 
0489 /*!
0490     Store what is about to be inserted to make sure it actually happens
0491 
0492     \sa rowsRemoved()
0493  */
0494 void ModelTest::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
0495 {
0496     Changing c;
0497     c.parent = parent;
0498     c.oldSize = model->rowCount(parent);
0499     c.last = model->data(model->index(start - 1, 0, parent));
0500     c.next = model->data(model->index(end + 1, 0, parent));
0501     remove.push(c);
0502 }
0503 
0504 /*!
0505     Confirm that what was said was going to happen actually did
0506 
0507     \sa rowsAboutToBeRemoved()
0508  */
0509 void ModelTest::rowsRemoved(const QModelIndex &parent, int start, int end)
0510 {
0511     Changing c = remove.pop();
0512     Q_ASSERT(c.parent == parent);
0513     Q_ASSERT(c.oldSize - (end - start + 1) == model->rowCount(parent));
0514     Q_ASSERT(c.last == model->data(model->index(start - 1, 0, c.parent)));
0515     Q_ASSERT(c.next == model->data(model->index(start, 0, c.parent)));
0516 }
0517