File indexing completed on 2024-05-12 16:42:19

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