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