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