ctkModelTester.cpp

Go to the documentation of this file.
00001 /*=========================================================================
00002 
00003   Library:   CTK
00004  
00005   Copyright (c) 2010  Kitware Inc.
00006 
00007   Licensed under the Apache License, Version 2.0 (the "License");
00008   you may not use this file except in compliance with the License.
00009   You may obtain a copy of the License at
00010 
00011       http://www.commontk.org/LICENSE
00012 
00013   Unless required by applicable law or agreed to in writing, software
00014   distributed under the License is distributed on an "AS IS" BASIS,
00015   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00016   See the License for the specific language governing permissions and
00017   limitations under the License.
00018  
00019 =========================================================================*/
00020 
00021 // Qt includes
00022 #include <QDebug>
00023 #include <QStack>
00024 
00025 // CTK includes
00026 #include "ctkModelTester.h"
00027 
00028 //-----------------------------------------------------------------------------
00029 class ctkModelTesterPrivate: public ctkPrivate<ctkModelTester>
00030 {
00031 public:
00032   ctkModelTesterPrivate();
00033   QAbstractItemModel *Model;
00034   bool ThrowOnError;
00035   bool NestedInserts;
00036 
00037   struct Change
00038   {
00039     QModelIndex Parent;
00040     Qt::Orientation Orientation;
00041     int Start;
00042     int End;
00043     
00044     int Count;
00045     QList<QPersistentModelIndex> Items;
00046   };
00047 
00048   QStack<Change> AboutToBeInserted;
00049   QStack<Change> AboutToBeRemoved;
00050   QList<QPersistentModelIndex> LayoutAboutToBeChanged;
00051 };
00052 
00053 //-----------------------------------------------------------------------------
00054 // ctkModelTesterPrivate methods
00055 
00056 //-----------------------------------------------------------------------------
00057 ctkModelTesterPrivate::ctkModelTesterPrivate()
00058 {
00059   this->Model = 0;
00060   this->ThrowOnError = true;
00061   this->NestedInserts = false;
00062 }
00063 
00064 //-----------------------------------------------------------------------------
00065 // ctkModelTester methods
00066 
00067 //-----------------------------------------------------------------------------
00068 ctkModelTester::ctkModelTester(QObject *_parent)
00069   :QObject(_parent)
00070 {
00071   CTK_INIT_PRIVATE(ctkModelTester);
00072 }
00073 
00074 //-----------------------------------------------------------------------------
00075 ctkModelTester::ctkModelTester(QAbstractItemModel *_model, QObject *_parent)
00076   :QObject(_parent)
00077 {
00078   CTK_INIT_PRIVATE(ctkModelTester);
00079   this->setModel(_model);
00080 }
00081 
00082 //-----------------------------------------------------------------------------
00083 void ctkModelTester::setModel(QAbstractItemModel *_model)
00084 {
00085   CTK_D(ctkModelTester);
00086   if (d->Model)
00087     {
00088     // disconnect
00089     d->Model->disconnect(this);
00090     d->AboutToBeInserted.clear();
00091     d->AboutToBeRemoved.clear();
00092     d->LayoutAboutToBeChanged.clear();
00093     }
00094   if (_model)
00095     {
00096     connect(_model, SIGNAL(columnsAboutToBeInserted(const QModelIndex &, int, int)),
00097             this, SLOT(onColumnsAboutToBeInserted(const QModelIndex& , int, int)));
00098     connect(_model, SIGNAL(columnsAboutToBeRemoved(const QModelIndex &, int, int)),
00099             this, SLOT(onColumnsAboutToBeRemoved(const QModelIndex& , int, int)));
00100     connect(_model, SIGNAL(columnsInserted(const QModelIndex &, int, int)),
00101             this, SLOT(onColumnsInserted(const QModelIndex& , int, int)));
00102     connect(_model, SIGNAL(columnsRemoved(const QModelIndex &, int, int)),
00103             this, SLOT(onColumnsRemoved(const QModelIndex& , int, int)));
00104     connect(_model, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)),
00105             this, SLOT(onDataChanged(const QModelIndex& , const QModelIndex &)));
00106     connect(_model, SIGNAL(layoutAboutToBeChanged()), this, SLOT(onLayoutAboutToBeChanged()));
00107     connect(_model, SIGNAL(layoutChanged()), this, SLOT(onLayoutChanged()));
00108     connect(_model, SIGNAL(modelAboutToBeReset()), this, SLOT(onModelAboutToBeReset()));
00109     connect(_model, SIGNAL(modelReset()), this, SLOT(onModelReset()));
00110     connect(_model, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)),
00111             this, SLOT(onRowsAboutToBeInserted(const QModelIndex& , int, int)));
00112     connect(_model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)),
00113             this, SLOT(onRowsAboutToBeRemoved(const QModelIndex& , int, int)));
00114     connect(_model, SIGNAL(rowsInserted(const QModelIndex &, int, int)),
00115             this, SLOT(onRowsInserted(const QModelIndex& , int, int)));
00116     connect(_model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)),
00117             this, SLOT(onRowsRemoved(const QModelIndex& , int, int)));
00118     }
00119   d->Model = _model;
00120   this->testModel();
00121 }
00122 
00123 //-----------------------------------------------------------------------------
00124 QAbstractItemModel* ctkModelTester::model()const
00125 {
00126   return ctk_d()->Model;
00127 }
00128 
00129 //-----------------------------------------------------------------------------
00130 void ctkModelTester::setThrowOnError(bool throwException)
00131 {
00132   ctk_d()->ThrowOnError = throwException;
00133 }
00134 
00135 //-----------------------------------------------------------------------------
00136 bool ctkModelTester::throwOnError()const
00137 {
00138   return ctk_d()->ThrowOnError;
00139 }
00140 
00141 //-----------------------------------------------------------------------------
00142 void ctkModelTester::setNestedInserts( bool nestedInsertsValue )
00143 {
00144   ctk_d()->NestedInserts = nestedInsertsValue;
00145 }
00146 
00147 //-----------------------------------------------------------------------------
00148 bool ctkModelTester::nestedInserts()const
00149 {
00150   return ctk_d()->NestedInserts;
00151 }
00152 
00153 //-----------------------------------------------------------------------------
00154 void  ctkModelTester::test(bool result, const QString& errorString)const
00155 {
00156   if (result)
00157     {
00158     return;
00159     }
00160   qDebug() << errorString;
00161   if (this->throwOnError())
00162     {
00163     throw errorString;
00164     }
00165 }
00166 
00167 //-----------------------------------------------------------------------------
00168 void ctkModelTester::testModelIndex(const QModelIndex& index)const
00169 {
00170   CTK_D(const ctkModelTester);
00171   if (!index.isValid())
00172     {// invalid index
00173     this->test(index.model() == 0, "An invalid index can't have a valid model.");
00174     this->test(index.model() != d->Model, "An invalid index can't have a valid model.");
00175     this->test(index.column() == -1, "An invalid index can't have a valid column.");
00176     this->test(index.row() == -1, "An invalid index can't have a valid row.");
00177     this->test(index.parent().isValid() == false, "An invalid index can't have a valid row.");
00178     this->test(index.row() == -1, "An invalid index can't have a valid row.");
00179     for(int i = 0; i < 100; ++i)
00180       {
00181       this->test(index.sibling(i % 10, i / 10).isValid() == false, "An invalid index can't have valid sibling.");
00182       }
00183     }
00184   else
00185     {// valid index
00186     this->test(index.model() == d->Model, "A valid index must have a valid model.");
00187     this->test(index.column() >= 0, "An valid index can't have an invalid column.");
00188     this->test(index.row() >= 0, "An valid index can't have an invalid row.");
00189     this->test(index == index.sibling(index.row(), index.column()), "Index's row and/or column is wrong.");
00190     }
00191   this->testData(index);
00192   this->testParent(index);
00193 }
00194 
00195 //-----------------------------------------------------------------------------
00196 void ctkModelTester::testData(const QModelIndex& index)const
00197 {
00198   if (!index.isValid())
00199     {
00200     this->test(!index.data(Qt::DisplayRole).isValid(), 
00201                QString("An invalid index can't have valid data: %1")
00202                .arg(index.data(Qt::DisplayRole).toString()));
00203     }
00204   else
00205     {
00206     this->test(index.data(Qt::DisplayRole).isValid(), 
00207                QString("A valid index can't have invalid data: %1, %2, %3")
00208                .arg(index.row()).arg(index.column()).arg(long(index.internalPointer())));
00209     }
00210 }
00211 
00212 //-----------------------------------------------------------------------------
00213 void ctkModelTester::testParent(const QModelIndex& vparent)const
00214 {
00215   CTK_D(const ctkModelTester);
00216   if (!d->Model->hasChildren(vparent))
00217     {
00218     // it's asking a lot :-)
00219     //this->test(d->Model->columnCount(vparent) <= 0, "A parent with no children can't have a columnCount > 0.");
00220     this->test(d->Model->rowCount(vparent) <= 0, "A parent with no children can't have a rowCount > 0.");
00221     }
00222   else
00223     {
00224     this->test(d->Model->columnCount(vparent) > 0, "A parent with children can't have a columnCount <= 0.");
00225     this->test(d->Model->rowCount(vparent) > 0 || d->Model->canFetchMore(vparent), "A parent with children can't have a rowCount <= 0. or if it does, canFetchMore should return true");
00226     }
00227 
00228   if (!vparent.isValid())
00229     {// otherwise there will be an infinite loop
00230     return;
00231     }
00232   
00233   for (int i = 0 ; i < d->Model->rowCount(vparent); ++i)
00234     {
00235     for (int j = 0; j < d->Model->columnCount(vparent); ++j)
00236       {
00237       this->test(d->Model->hasIndex(i, j, vparent), "hasIndex should return true for int range {0->rowCount(), 0->columnCount()}");
00238       QModelIndex child = vparent.child(i, j);
00239       this->test(child.parent() == vparent, "A child's parent can't be different from its parent");
00240       this->testModelIndex(child);
00241       }
00242     }
00243 }
00244 //-----------------------------------------------------------------------------
00245 void ctkModelTester::testPersistentModelIndex(const QPersistentModelIndex& index)const
00246 {
00247   CTK_D(const ctkModelTester);
00248   //qDebug() << "Test persistent Index: " << index ;
00249   this->test(index.isValid(), "Persistent model index can't be invalid");
00250   // did you forget to call QAbstractItemModel::changePersistentIndex() between 
00251   // beginLayoutChanged() and changePersistentIndex() ?
00252   QModelIndex modelIndex = d->Model->index(index.row(), index.column(), index.parent());
00253   this->test(modelIndex == index, 
00254              QString("Persistent index (%1, %2) can't be invalid").arg(index.row()).arg(index.column()));
00255 }
00256 
00257 //-----------------------------------------------------------------------------
00258 void ctkModelTester::testModel()const
00259 {
00260   CTK_D(const ctkModelTester);
00261   if (d->Model == 0)
00262     {
00263     return;
00264     }
00265   for (int i = 0 ; i < d->Model->rowCount(); ++i)
00266     {
00267     for (int j = 0; j < d->Model->columnCount(); ++j)
00268       {
00269       this->test(d->Model->hasIndex(i, j), "hasIndex should return true for int range {0->rowCount(), 0->columnCount()}");
00270       QModelIndex child = d->Model->index(i, j);
00271       this->test(!child.parent().isValid(), "A child's parent can't be different from its parent");
00272       this->testModelIndex(child);
00273       }
00274     }
00275 }
00276 
00277 //-----------------------------------------------------------------------------
00278 void ctkModelTester::onColumnsAboutToBeInserted(const QModelIndex & vparent, int start, int end)
00279 {
00280   //qDebug() << "columnsAboutToBeInserted: " << vparent << start << end;
00281   this->onItemsAboutToBeInserted(vparent, Qt::Horizontal, start, end);
00282 }
00283 
00284 //-----------------------------------------------------------------------------
00285 void ctkModelTester::onColumnsAboutToBeRemoved(const QModelIndex & vparent, int start, int end)
00286 {
00287   //qDebug() << "columnsAboutToBeRemoved: " << vparent << start << end;
00288   this->onItemsAboutToBeRemoved(vparent, Qt::Horizontal, start, end);
00289 }
00290 
00291 //-----------------------------------------------------------------------------
00292 void ctkModelTester::onColumnsInserted(const QModelIndex & vparent, int start, int end)
00293 {
00294   //qDebug() << "columnsInserted: " << vparent << start << end;
00295   this->onItemsInserted(vparent, Qt::Horizontal, start, end);
00296 }
00297 
00298 //-----------------------------------------------------------------------------
00299 void ctkModelTester::onColumnsRemoved(const QModelIndex & vparent, int start, int end)
00300 {
00301   //qDebug() << "columnsRemoved: " << vparent << start << end;
00302   this->onItemsRemoved(vparent, Qt::Horizontal, start, end);
00303 }
00304 
00305 //-----------------------------------------------------------------------------
00306 void ctkModelTester::onDataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight)
00307 {
00308   this->test(topLeft.parent() == bottomRight.parent(), "DataChanged support items with the same parent only");
00309   this->test(topLeft.row() >= bottomRight.row(), "topLeft can't have a row lower than bottomRight");
00310   this->test(bottomRight.column() >= topLeft.column(), "topLeft can't have a column lower than bottomRight");
00311   for (int i = topLeft.row(); i <= bottomRight.row(); ++i)
00312     {
00313     for (int j = topLeft.column(); j < bottomRight.column(); ++j)
00314       {
00315       this->test(topLeft.sibling(i,j).isValid(), "Changed data must be valid");
00316       // do the test on the indexes here, it's easier to debug than in testModel();
00317       this->testModelIndex(topLeft.sibling(i,j));
00318       }
00319     }
00320   this->testModel();
00321 }
00322 
00323 //-----------------------------------------------------------------------------
00324 void ctkModelTester::onHeaderDataChanged(Qt::Orientation orientation, int first, int last)
00325 {
00326   CTK_D(ctkModelTester);
00327   this->test(first <= last, "Changed headers have wrong indexes");
00328   switch (orientation)
00329     {
00330     case Qt::Horizontal:
00331       this->test(d->Model->columnCount() > last, "There is no more horizontal headers than columns.");
00332       break;
00333     case Qt::Vertical:
00334       this->test(d->Model->rowCount() > last, "There is no more vertical headers than rows.");
00335       break;
00336     default:
00337       this->test(orientation == Qt::Horizontal || orientation == Qt::Vertical, "Wrong orientation.");
00338       break;
00339     }
00340   this->testModel();
00341 }
00342 
00343 //-----------------------------------------------------------------------------
00344 QList<QPersistentModelIndex> ctkModelTester::persistentModelIndexes(const QModelIndex& index)const
00345 {
00346   CTK_D(const ctkModelTester);
00347   QList<QPersistentModelIndex> list;
00348   for (int i = 0; i < d->Model->rowCount(index); ++i)
00349     {
00350     for (int j = 0; j < d->Model->columnCount(index); ++j)
00351       {
00352       QPersistentModelIndex child = d->Model->index(i, j, index);
00353       list.append(child);
00354       list += this->ctkModelTester::persistentModelIndexes(child);
00355       }
00356     }
00357   return list;
00358 }
00359 
00360 //-----------------------------------------------------------------------------
00361 void ctkModelTester::onLayoutAboutToBeChanged()
00362 {
00363   CTK_D(ctkModelTester);
00364 
00365   d->LayoutAboutToBeChanged = this->persistentModelIndexes(QModelIndex());
00366   this->testModel();
00367 }
00368 
00369 //-----------------------------------------------------------------------------
00370 void ctkModelTester::onLayoutChanged()
00371 {
00372   CTK_D(ctkModelTester);
00373   foreach (const QPersistentModelIndex& index, d->LayoutAboutToBeChanged)
00374     {
00375     this->testPersistentModelIndex(index);
00376     }
00377   d->LayoutAboutToBeChanged.clear();
00378   this->testModel();
00379 }
00380 
00381 //-----------------------------------------------------------------------------
00382 void ctkModelTester::onModelAboutToBeReset()
00383 {
00384   this->testModel();
00385 }
00386 
00387 //-----------------------------------------------------------------------------
00388 void ctkModelTester::onModelReset()
00389 {
00390   this->testModel();
00391 }
00392 
00393 //-----------------------------------------------------------------------------
00394 void ctkModelTester::onRowsAboutToBeInserted(const QModelIndex &vparent, int start, int end)
00395 {
00396   //qDebug() << "rowsAboutToBeInserted: " << vparent << start << end;
00397   this->onItemsAboutToBeInserted(vparent, Qt::Vertical, start, end);
00398 }
00399 
00400 //-----------------------------------------------------------------------------
00401 void ctkModelTester::onRowsAboutToBeRemoved(const QModelIndex &vparent, int start, int end)
00402 {
00403   //qDebug() << "rowsAboutToBeRemoved: " << vparent << start << end;
00404   this->onItemsAboutToBeRemoved(vparent, Qt::Vertical, start, end);
00405 }
00406 
00407 //-----------------------------------------------------------------------------
00408 void ctkModelTester::onRowsInserted(const QModelIndex & vparent, int start, int end)
00409 {
00410   //qDebug() << "rowsInserted: " << vparent << start << end;
00411   this->onItemsInserted(vparent, Qt::Vertical, start, end);
00412 }
00413 
00414 //-----------------------------------------------------------------------------
00415 void ctkModelTester::onRowsRemoved(const QModelIndex & vparent, int start, int end)
00416 {
00417   //qDebug() << "rowsRemoved: " << vparent << start << end;
00418   this->onItemsRemoved(vparent, Qt::Vertical, start, end);
00419 }
00420 
00421 //-----------------------------------------------------------------------------
00422 void ctkModelTester::onItemsAboutToBeInserted(const QModelIndex &vparent, Qt::Orientation orientation, int start, int end)
00423 {
00424   CTK_D(ctkModelTester);
00425   this->test(start <= end, "Start can't be higher than end");
00426   //Not sure about that
00427   if (!d->NestedInserts)
00428     {
00429     this->test(d->AboutToBeInserted.size() == 0, "While inserting items, you can't insert other items.");
00430     }
00431   //Not sure about that
00432   this->test(d->AboutToBeRemoved.size() == 0, "While removing items, you can't insert other items.");
00433 
00434   ctkModelTesterPrivate::Change change;
00435   change.Parent = vparent;
00436   change.Orientation = orientation;
00437   change.Start = start;
00438   change.End = end;
00439   change.Count = (orientation == Qt::Vertical ? d->Model->rowCount(vparent) :d->Model->columnCount(vparent) );
00440   change.Items = this->persistentModelIndexes(vparent);
00441   d->AboutToBeInserted.push(change);
00442   
00443   this->testModel();
00444 }
00445 
00446 //-----------------------------------------------------------------------------
00447 void ctkModelTester::onItemsAboutToBeRemoved(const QModelIndex &vparent, Qt::Orientation orientation, int start, int end)
00448 {
00449   CTK_D(ctkModelTester);
00450   this->test(start <= end, "Start can't be higher than end");
00451   //Not sure about that
00452   this->test(d->AboutToBeInserted.size() == 0, "While inserting items, you can't remove other items.");
00453   //Not sure about that
00454   this->test(d->AboutToBeRemoved.size() == 0, "While removing items, you can't remove other items.");
00455   
00456   int count = (orientation == Qt::Vertical ? d->Model->rowCount(vparent) :d->Model->columnCount(vparent) );
00457   this->test(start < count, "Item to remove can't be invalid");
00458   this->test(end < count, "Item to remove can't be invalid");
00459   
00460   ctkModelTesterPrivate::Change change;
00461   change.Parent = vparent;
00462   change.Orientation = orientation;
00463   change.Start = start;
00464   change.End = end;
00465   change.Count = count;
00466   for (int i = 0 ; i < count; ++i)
00467     {
00468     QPersistentModelIndex index;
00469     index = (orientation == Qt::Vertical ? d->Model->index(i, 0, vparent) : d->Model->index(0, i, vparent));
00470     this->test(index.isValid(), "Index invalid");
00471     if (orientation == Qt::Vertical && (index.row() < start || index.row() > end))
00472       {
00473       change.Items.append(index);
00474       }
00475     if (orientation == Qt::Horizontal && (index.column() < start || index.column() > end))
00476       {
00477       change.Items.append(index);
00478       }
00479     }
00480   d->AboutToBeRemoved.push(change);
00481 
00482   this->testModel();
00483   //qDebug() << "About to be removed: " << start << " " << end <<vparent << count << change.Items.count();
00484 }
00485 
00486 //-----------------------------------------------------------------------------
00487 void ctkModelTester::onItemsInserted(const QModelIndex & vparent, Qt::Orientation orientation, int start, int end)
00488 {
00489   CTK_D(ctkModelTester);
00490   this->test(start <= end, "Start can't be higher end");
00491   this->test(d->AboutToBeInserted.size() != 0, "rowsInserted() has been emitted, but not rowsAboutToBeInserted.");
00492   //Not sure about that
00493   this->test(d->AboutToBeRemoved.size() == 0, "While removing items, you can't insert other items.");
00494 
00495   ctkModelTesterPrivate::Change change = d->AboutToBeInserted.pop();
00496   this->test(change.Parent == vparent, "Parent can't be different");
00497   this->test(change.Orientation == Qt::Vertical, "Orientation can't be different");
00498   this->test(change.Start == start, "Start can't be different");
00499   this->test(change.End == end, "End can't be different");
00500   int count =  (orientation == Qt::Vertical ? d->Model->rowCount(vparent) :d->Model->columnCount(vparent) );
00501   this->test(change.Count < count, "The new count number can't be lower");
00502   this->test(count - change.Count == (end - start + 1) , "The new count number can't be lower");
00503   foreach(const QPersistentModelIndex& index, change.Items)
00504     {
00505     this->testPersistentModelIndex(index);
00506     }
00507   change.Items.clear();
00508   
00509   this->testModel();
00510 }
00511 
00512 //-----------------------------------------------------------------------------
00513 void ctkModelTester::onItemsRemoved(const QModelIndex & vparent, Qt::Orientation orientation, int start, int end)
00514 { 
00515   CTK_D(ctkModelTester);
00516   this->test(start <= end, "Start can't be higher end");
00517   this->test(d->AboutToBeRemoved.size() != 0, "rowsRemoved() has been emitted, but not rowsAboutToBeRemoved.");
00518   //Not sure about that
00519   this->test(d->AboutToBeInserted.size() == 0, "While inserted items, you can't remove other items.");
00520 
00521   ctkModelTesterPrivate::Change change = d->AboutToBeRemoved.pop();
00522   this->test(change.Parent == vparent, "Parent can't be different");
00523   this->test(change.Orientation == Qt::Vertical, "Orientation can't be different");
00524   this->test(change.Start == start, "Start can't be different");
00525   this->test(change.End == end, "End can't be different");
00526   int count = (orientation == Qt::Vertical ? d->Model->rowCount(vparent) :d->Model->columnCount(vparent) );
00527   this->test(change.Count > count, "The new count number can't be higher");
00528   this->test(change.Count - count == (end - start + 1) , "The new count number can't be higher");
00529   foreach(const QPersistentModelIndex& index, change.Items)
00530     {
00531     this->testPersistentModelIndex(index);
00532     }
00533   change.Items.clear();
00534   
00535   this->testModel();
00536 }
00537 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines

Generated on 21 May 2010 for CTK by  doxygen 1.6.1