File indexing completed on 2024-05-05 04:58:31

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