oopapidocs  2.0
modeltest.cpp
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 
 All Classes Namespaces Functions Enumerations