oopapidocs
2.0
|
00001 /**************************************************************************** 00002 ** 00003 ** Copyright (C) 2007 Trolltech ASA. All rights reserved. 00004 ** 00005 ** This file is part of the Qt Concurrent project on Trolltech Labs. 00006 ** 00007 ** This file may be used under the terms of the GNU General Public 00008 ** License version 2.0 as published by the Free Software Foundation 00009 ** and appearing in the file LICENSE.GPL included in the packaging of 00010 ** this file. Please review the following information to ensure GNU 00011 ** General Public Licensing requirements will be met: 00012 ** http://www.trolltech.com/products/qt/opensource.html 00013 ** 00014 ** If you are unsure which license is appropriate for your use, please 00015 ** review the following information: 00016 ** http://www.trolltech.com/products/qt/licensing.html or contact the 00017 ** sales department at sales@trolltech.com. 00018 ** 00019 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE 00020 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 00021 ** 00022 ****************************************************************************/ 00023 00024 #include <QtGui/QtGui> 00025 00026 #include "modeltest.h" 00027 00028 Q_DECLARE_METATYPE(QModelIndex) 00029 00030 00033 ModelTest::ModelTest(QAbstractItemModel *_model, QObject *parent) : QObject(parent), model(_model), fetchingMore(false) 00034 { 00035 Q_ASSERT(model); 00036 00037 connect(model, SIGNAL(columnsAboutToBeInserted(const QModelIndex &, int, int)), 00038 this, SLOT(runAllTests())); 00039 connect(model, SIGNAL(columnsAboutToBeRemoved(const QModelIndex &, int, int)), 00040 this, SLOT(runAllTests())); 00041 connect(model, SIGNAL(columnsInserted(const QModelIndex &, int, int)), 00042 this, SLOT(runAllTests())); 00043 connect(model, SIGNAL(columnsRemoved(const QModelIndex &, int, int)), 00044 this, SLOT(runAllTests())); 00045 connect(model, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), 00046 this, SLOT(runAllTests())); 00047 connect(model, SIGNAL(headerDataChanged(Qt::Orientation, int, int)), 00048 this, SLOT(runAllTests())); 00049 connect(model, SIGNAL(layoutAboutToBeChanged ()), this, SLOT(runAllTests())); 00050 connect(model, SIGNAL(layoutChanged ()), this, SLOT(runAllTests())); 00051 connect(model, SIGNAL(modelReset ()), this, SLOT(runAllTests())); 00052 connect(model, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)), 00053 this, SLOT(runAllTests())); 00054 connect(model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)), 00055 this, SLOT(runAllTests())); 00056 connect(model, SIGNAL(rowsInserted(const QModelIndex &, int, int)), 00057 this, SLOT(runAllTests())); 00058 connect(model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), 00059 this, SLOT(runAllTests())); 00060 00061 // Special checks for inserting/removing 00062 connect(model, SIGNAL(layoutAboutToBeChanged()), 00063 this, SLOT(layoutAboutToBeChanged())); 00064 connect(model, SIGNAL(layoutChanged()), 00065 this, SLOT(layoutChanged())); 00066 00067 connect(model, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)), 00068 this, SLOT(rowsAboutToBeInserted(const QModelIndex &, int, int))); 00069 connect(model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)), 00070 this, SLOT(rowsAboutToBeRemoved(const QModelIndex &, int, int))); 00071 connect(model, SIGNAL(rowsInserted(const QModelIndex &, int, int)), 00072 this, SLOT(rowsInserted(const QModelIndex &, int, int))); 00073 connect(model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), 00074 this, SLOT(rowsRemoved(const QModelIndex &, int, int))); 00075 00076 runAllTests(); 00077 } 00078 00079 void ModelTest::runAllTests() 00080 { 00081 if (fetchingMore) 00082 return; 00083 nonDestructiveBasicTest(); 00084 rowCount(); 00085 columnCount(); 00086 hasIndex(); 00087 index(); 00088 parent(); 00089 data(); 00090 } 00091 00096 void ModelTest::nonDestructiveBasicTest() 00097 { 00098 Q_ASSERT(model->buddy(QModelIndex()) == QModelIndex()); 00099 model->canFetchMore(QModelIndex()); 00100 Q_ASSERT(model->columnCount(QModelIndex()) >= 0); 00101 Q_ASSERT(model->data(QModelIndex()) == QVariant()); 00102 fetchingMore = true; 00103 model->fetchMore(QModelIndex()); 00104 fetchingMore = false; 00105 Qt::ItemFlags flags = model->flags(QModelIndex()); 00106 Q_ASSERT(flags == Qt::ItemIsDropEnabled || flags == 0); 00107 model->hasChildren(QModelIndex()); 00108 model->hasIndex(0, 0); 00109 model->headerData(0, Qt::Horizontal); 00110 model->index(0, 0); 00111 Q_ASSERT(model->index(-1, -1) == QModelIndex()); 00112 model->itemData(QModelIndex()); 00113 QVariant cache; 00114 model->match(QModelIndex(), -1, cache); 00115 model->mimeTypes(); 00116 Q_ASSERT(model->parent(QModelIndex()) == QModelIndex()); 00117 Q_ASSERT(model->rowCount() >= 0); 00118 QVariant variant; 00119 model->setData(QModelIndex(), variant, -1); 00120 model->setHeaderData(-1, Qt::Horizontal, QVariant()); 00121 model->setHeaderData(0, Qt::Horizontal, QVariant()); 00122 model->setHeaderData(999999, Qt::Horizontal, QVariant()); 00123 QMap<int, QVariant> roles; 00124 model->sibling(0, 0, QModelIndex()); 00125 model->span(QModelIndex()); 00126 model->supportedDropActions(); 00127 } 00128 00134 void ModelTest::rowCount() 00135 { 00136 // check top row 00137 QModelIndex topIndex = model->index(0, 0, QModelIndex()); 00138 int rows = model->rowCount(topIndex); 00139 Q_ASSERT(rows >= 0); 00140 if (rows > 0) 00141 Q_ASSERT(model->hasChildren(topIndex) == true); 00142 00143 QModelIndex secondLevelIndex = model->index(0, 0, topIndex); 00144 if (secondLevelIndex.isValid()) { // not the top level 00145 // check a row count where parent is valid 00146 rows = model->rowCount(secondLevelIndex); 00147 Q_ASSERT(rows >= 0); 00148 if (rows > 0) 00149 Q_ASSERT(model->hasChildren(secondLevelIndex) == true); 00150 } 00151 00152 // The models rowCount() is tested more extensively in checkChildren(), 00153 // but this catches the big mistakes 00154 } 00155 00159 void ModelTest::columnCount() 00160 { 00161 // check top row 00162 QModelIndex topIndex = model->index(0, 0, QModelIndex()); 00163 Q_ASSERT(model->columnCount(topIndex) >= 0); 00164 00165 // check a column count where parent is valid 00166 QModelIndex childIndex = model->index(0, 0, topIndex); 00167 if (childIndex.isValid()) 00168 Q_ASSERT(model->columnCount(childIndex) >= 0); 00169 00170 // columnCount() is tested more extensively in checkChildren(), 00171 // but this catches the big mistakes 00172 } 00173 00177 void ModelTest::hasIndex() 00178 { 00179 // Make sure that invalid values returns an invalid index 00180 Q_ASSERT(model->hasIndex(-2, -2) == false); 00181 Q_ASSERT(model->hasIndex(-2, 0) == false); 00182 Q_ASSERT(model->hasIndex(0, -2) == false); 00183 00184 int rows = model->rowCount(); 00185 int columns = model->columnCount(); 00186 00187 // check out of bounds 00188 Q_ASSERT(model->hasIndex(rows, columns) == false); 00189 Q_ASSERT(model->hasIndex(rows + 1, columns + 1) == false); 00190 00191 if (rows > 0) 00192 Q_ASSERT(model->hasIndex(0, 0) == true); 00193 00194 // hasIndex() is tested more extensively in checkChildren(), 00195 // but this catches the big mistakes 00196 } 00197 00201 void ModelTest::index() 00202 { 00203 // Make sure that invalid values returns an invalid index 00204 Q_ASSERT(model->index(-2, -2) == QModelIndex()); 00205 Q_ASSERT(model->index(-2, 0) == QModelIndex()); 00206 Q_ASSERT(model->index(0, -2) == QModelIndex()); 00207 00208 int rows = model->rowCount(); 00209 int columns = model->columnCount(); 00210 00211 if (rows == 0) 00212 return; 00213 00214 // Catch off by one errors 00215 Q_ASSERT(model->index(rows, columns) == QModelIndex()); 00216 Q_ASSERT(model->index(0, 0).isValid() == true); 00217 00218 // Make sure that the same index is *always* returned 00219 QModelIndex a = model->index(0, 0); 00220 QModelIndex b = model->index(0, 0); 00221 Q_ASSERT(a == b); 00222 00223 // index() is tested more extensively in checkChildren(), 00224 // but this catches the big mistakes 00225 } 00226 00230 void ModelTest::parent() 00231 { 00232 // Make sure the model wont crash and will return an invalid QModelIndex 00233 // when asked for the parent of an invalid index. 00234 Q_ASSERT(model->parent(QModelIndex()) == QModelIndex()); 00235 00236 if (model->rowCount() == 0) 00237 return; 00238 00239 // Column 0 | Column 1 | 00240 // QModelIndex() | | 00241 // \- topIndex | topIndex1 | 00242 // \- childIndex | childIndex1 | 00243 00244 // Common error test #1, make sure that a top level index has a parent 00245 // that is a invalid QModelIndex. 00246 QModelIndex topIndex = model->index(0, 0, QModelIndex()); 00247 Q_ASSERT(model->parent(topIndex) == QModelIndex()); 00248 00249 // Common error test #2, make sure that a second level index has a parent 00250 // that is the first level index. 00251 if (model->rowCount(topIndex) > 0) { 00252 QModelIndex childIndex = model->index(0, 0, topIndex); 00253 Q_ASSERT(model->parent(childIndex) == topIndex); 00254 } 00255 00256 // Common error test #3, the second column should NOT have the same children 00257 // as the first column in a row. 00258 // Usually the second column shouldn't have children. 00259 QModelIndex topIndex1 = model->index(0, 1, QModelIndex()); 00260 if (model->rowCount(topIndex1) > 0) { 00261 QModelIndex childIndex = model->index(0, 0, topIndex); 00262 QModelIndex childIndex1 = model->index(0, 0, topIndex1); 00263 Q_ASSERT(childIndex != childIndex1); 00264 } 00265 00266 // Full test, walk n levels deep through the model making sure that all 00267 // parent's children correctly specify their parent. 00268 checkChildren(QModelIndex()); 00269 } 00270 00285 void ModelTest::checkChildren(const QModelIndex &parent, int currentDepth) 00286 { 00287 // First just try walking back up the tree. 00288 QModelIndex p = parent; 00289 while (p.isValid()) 00290 p = p.parent(); 00291 00292 // For models that are dynamically populated 00293 if (model->canFetchMore(parent)) { 00294 fetchingMore = true; 00295 model->fetchMore(parent); 00296 fetchingMore = false; 00297 } 00298 00299 int rows = model->rowCount(parent); 00300 int columns = model->columnCount(parent); 00301 00302 if (rows > 0) 00303 Q_ASSERT(model->hasChildren(parent)); 00304 00305 // Some further testing against rows(), columns(), and hasChildren() 00306 Q_ASSERT(rows >= 0); 00307 Q_ASSERT(columns >= 0); 00308 if (rows > 0) 00309 Q_ASSERT(model->hasChildren(parent) == true); 00310 00311 //qDebug() << "parent:" << model->data(parent).toString() << "rows:" << rows 00312 // << "columns:" << columns << "parent column:" << parent.column(); 00313 00314 Q_ASSERT(model->hasIndex(rows + 1, 0, parent) == false); 00315 for (int r = 0; r < rows; ++r) { 00316 if (model->canFetchMore(parent)) { 00317 fetchingMore = true; 00318 model->fetchMore(parent); 00319 fetchingMore = false; 00320 } 00321 Q_ASSERT(model->hasIndex(r, columns + 1, parent) == false); 00322 for (int c = 0; c < columns; ++c) { 00323 Q_ASSERT(model->hasIndex(r, c, parent) == true); 00324 QModelIndex index = model->index(r, c, parent); 00325 // rowCount() and columnCount() said that it existed... 00326 Q_ASSERT(index.isValid() == true); 00327 00328 // index() should always return the same index when called twice in a row 00329 QModelIndex modifiedIndex = model->index(r, c, parent); 00330 Q_ASSERT(index == modifiedIndex); 00331 00332 // Make sure we get the same index if we request it twice in a row 00333 QModelIndex a = model->index(r, c, parent); 00334 QModelIndex b = model->index(r, c, parent); 00335 Q_ASSERT(a == b); 00336 00337 // Some basic checking on the index that is returned 00338 Q_ASSERT(index.model() == model); 00339 Q_ASSERT(index.row() == r); 00340 Q_ASSERT(index.column() == c); 00341 // While you can technically return a QVariant usually this is a sign 00342 // of an bug in data() Disable if this really is ok in your model. 00343 //Q_ASSERT(model->data(index, Qt::DisplayRole).isValid() == true); 00344 00345 // If the next test fails here is some somewhat useful debug you play with. 00346 /* 00347 if (model->parent(index) != parent) { 00348 qDebug() << r << c << currentDepth << model->data(index).toString() 00349 << model->data(parent).toString(); 00350 qDebug() << index << parent << model->parent(index); 00351 // And a view that you can even use to show the model. 00352 //QTreeView view; 00353 //view.setModel(model); 00354 //view.show(); 00355 }*/ 00356 00357 // Check that we can get back our real parent. 00358 QModelIndex p = model->parent(index); 00359 //qDebug() << "child:" << index; 00360 //qDebug() << p; 00361 //qDebug() << parent; 00362 Q_ASSERT(model->parent(index) == parent); 00363 00364 // recursively go down the children 00365 if (model->hasChildren(index) && currentDepth < 10 ) { 00366 //qDebug() << r << c << "has children" << model->rowCount(index); 00367 checkChildren(index, ++currentDepth); 00368 }/* else { if (currentDepth >= 10) qDebug() << "checked 10 deep"; };*/ 00369 00370 // make sure that after testing the children that the index doesn't change. 00371 QModelIndex newerIndex = model->index(r, c, parent); 00372 Q_ASSERT(index == newerIndex); 00373 } 00374 } 00375 } 00376 00380 void ModelTest::data() 00381 { 00382 // Invalid index should return an invalid qvariant 00383 Q_ASSERT(!model->data(QModelIndex()).isValid()); 00384 00385 if (model->rowCount() == 0) 00386 return; 00387 00388 // A valid index should have a valid QVariant data 00389 Q_ASSERT(model->index(0, 0).isValid()); 00390 00391 // shouldn't be able to set data on an invalid index 00392 Q_ASSERT(model->setData(QModelIndex(), QLatin1String("foo"), Qt::DisplayRole) == false); 00393 00394 // General Purpose roles that should return a QString 00395 QVariant variant = model->data(model->index(0, 0), Qt::ToolTipRole); 00396 if (variant.isValid()) { 00397 Q_ASSERT(qVariantCanConvert<QString>(variant)); 00398 } 00399 variant = model->data(model->index(0, 0), Qt::StatusTipRole); 00400 if (variant.isValid()) { 00401 Q_ASSERT(qVariantCanConvert<QString>(variant)); 00402 } 00403 variant = model->data(model->index(0, 0), Qt::WhatsThisRole); 00404 if (variant.isValid()) { 00405 Q_ASSERT(qVariantCanConvert<QString>(variant)); 00406 } 00407 00408 // General Purpose roles that should return a QSize 00409 variant = model->data(model->index(0, 0), Qt::SizeHintRole); 00410 if (variant.isValid()) { 00411 Q_ASSERT(qVariantCanConvert<QSize>(variant)); 00412 } 00413 00414 // General Purpose roles that should return a QFont 00415 QVariant fontVariant = model->data(model->index(0, 0), Qt::FontRole); 00416 if (fontVariant.isValid()) { 00417 Q_ASSERT(qVariantCanConvert<QFont>(fontVariant)); 00418 } 00419 00420 // Check that the alignment is one we know about 00421 QVariant textAlignmentVariant = model->data(model->index(0, 0), Qt::TextAlignmentRole); 00422 if (textAlignmentVariant.isValid()) { 00423 int alignment = textAlignmentVariant.toInt(); 00424 Q_ASSERT(alignment == Qt::AlignLeft || 00425 alignment == Qt::AlignRight || 00426 alignment == Qt::AlignHCenter || 00427 alignment == Qt::AlignJustify || 00428 alignment == Qt::AlignTop || 00429 alignment == Qt::AlignBottom || 00430 alignment == Qt::AlignVCenter || 00431 alignment == Qt::AlignCenter || 00432 alignment == Qt::AlignAbsolute || 00433 alignment == Qt::AlignLeading || 00434 alignment == Qt::AlignTrailing); 00435 } 00436 00437 // General Purpose roles that should return a QColor 00438 QVariant colorVariant = model->data(model->index(0, 0), Qt::BackgroundColorRole); 00439 if (colorVariant.isValid()) { 00440 Q_ASSERT(qVariantCanConvert<QColor>(colorVariant)); 00441 } 00442 00443 colorVariant = model->data(model->index(0, 0), Qt::TextColorRole); 00444 if (colorVariant.isValid()) { 00445 Q_ASSERT(qVariantCanConvert<QColor>(colorVariant)); 00446 } 00447 00448 // Check that the "check state" is one we know about. 00449 QVariant checkStateVariant = model->data(model->index(0, 0), Qt::CheckStateRole); 00450 if (checkStateVariant.isValid()) { 00451 int state = checkStateVariant.toInt(); 00452 Q_ASSERT(state == Qt::Unchecked || 00453 state == Qt::PartiallyChecked || 00454 state == Qt::Checked); 00455 } 00456 } 00457 00463 void ModelTest::rowsAboutToBeInserted(const QModelIndex &parent, int start, int end) 00464 { 00465 Q_UNUSED(end); 00466 Changing c; 00467 c.parent = parent; 00468 c.oldSize = model->rowCount(parent); 00469 c.last = model->data(model->index(start - 1, 0, parent)); 00470 c.next = model->data(model->index(start, 0, parent)); 00471 insert.push(c); 00472 } 00473 00479 void ModelTest::rowsInserted(const QModelIndex & parent, int start, int end) 00480 { 00481 Changing c = insert.pop(); 00482 Q_ASSERT(c.parent == parent); 00483 Q_ASSERT(c.oldSize + (end - start + 1) == model->rowCount(parent)); 00484 Q_ASSERT(c.last == model->data(model->index(start - 1, 0, c.parent))); 00485 /* 00486 if (c.next != model->data(model->index(end + 1, 0, c.parent))) { 00487 qDebug() << start << end; 00488 for (int i=0; i < model->rowCount(); ++i) 00489 qDebug() << model->index(i, 0).data().toString(); 00490 qDebug() << c.next << model->data(model->index(end + 1, 0, c.parent)); 00491 } 00492 */ 00493 Q_ASSERT(c.next == model->data(model->index(end + 1, 0, c.parent))); 00494 } 00495 00496 void ModelTest::layoutAboutToBeChanged() 00497 { 00498 for (int i = 0; i < qBound(0, model->rowCount(), 100); ++i) 00499 changing.append(QPersistentModelIndex(model->index(i, 0))); 00500 } 00501 00502 void ModelTest::layoutChanged() 00503 { 00504 for (int i = 0; i < changing.count(); ++i) { 00505 QPersistentModelIndex p = changing[i]; 00506 Q_ASSERT(p == model->index(p.row(), p.column(), p.parent())); 00507 } 00508 changing.clear(); 00509 } 00510 00516 void ModelTest::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) 00517 { 00518 Changing c; 00519 c.parent = parent; 00520 c.oldSize = model->rowCount(parent); 00521 c.last = model->data(model->index(start - 1, 0, parent)); 00522 c.next = model->data(model->index(end + 1, 0, parent)); 00523 remove.push(c); 00524 } 00525 00531 void ModelTest::rowsRemoved(const QModelIndex & parent, int start, int end) 00532 { 00533 Changing c = remove.pop(); 00534 Q_ASSERT(c.parent == parent); 00535 Q_ASSERT(c.oldSize - (end - start + 1) == model->rowCount(parent)); 00536 Q_ASSERT(c.last == model->data(model->index(start - 1, 0, c.parent))); 00537 Q_ASSERT(c.next == model->data(model->index(start, 0, c.parent))); 00538 } 00539