OGS
modeltest.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2007 Trolltech ASA. All rights reserved.
4 **
5 ** This file is part of the Qt Concurrent project on Trolltech Labs.
6 **
7 ** This file may be used under the terms of the GNU General Public
8 ** License version 2.0 as published by the Free Software Foundation
9 ** and appearing in the file LICENSE.GPL included in the packaging of
10 ** this file. Please review the following information to ensure GNU
11 ** General Public Licensing requirements will be met:
12 ** http://www.trolltech.com/products/qt/opensource.html
13 **
14 ** If you are unsure which license is appropriate for your use, please
15 ** review the following information:
16 ** http://www.trolltech.com/products/qt/licensing.html or contact the
17 ** sales department at sales@trolltech.com.
18 **
19 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
20 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
21 **
22 ****************************************************************************/
23 
24 #include "modeltest.h"
25 
26 #include <QtGui/QtGui>
27 
28 Q_DECLARE_METATYPE(QModelIndex)
29 
30 
34 ModelTest::ModelTest(QAbstractItemModel* _model, QObject* parent)
35  : QObject(parent), model(_model), fetchingMore(false)
36 {
37  Q_ASSERT(model);
38 
39  connect(model,
40  SIGNAL(columnsAboutToBeInserted(const QModelIndex&, int, int)),
41  this, SLOT(runAllTests()));
42  connect(model,
43  SIGNAL(columnsAboutToBeRemoved(const QModelIndex&, int, int)), this,
44  SLOT(runAllTests()));
45  connect(model, SIGNAL(columnsInserted(const QModelIndex&, int, int)), this,
46  SLOT(runAllTests()));
47  connect(model, SIGNAL(columnsRemoved(const QModelIndex&, int, int)), this,
48  SLOT(runAllTests()));
49  connect(model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)),
50  this, SLOT(runAllTests()));
51  connect(model, SIGNAL(headerDataChanged(Qt::Orientation, int, int)), this,
52  SLOT(runAllTests()));
53  connect(model, SIGNAL(layoutAboutToBeChanged()), this, SLOT(runAllTests()));
54  connect(model, SIGNAL(layoutChanged()), this, SLOT(runAllTests()));
55  connect(model, SIGNAL(modelReset()), this, SLOT(runAllTests()));
56  connect(model, SIGNAL(rowsAboutToBeInserted(const QModelIndex&, int, int)),
57  this, SLOT(runAllTests()));
58  connect(model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex&, int, int)),
59  this, SLOT(runAllTests()));
60  connect(model, SIGNAL(rowsInserted(const QModelIndex&, int, int)), this,
61  SLOT(runAllTests()));
62  connect(model, SIGNAL(rowsRemoved(const QModelIndex&, int, int)), this,
63  SLOT(runAllTests()));
64 
65  // Special checks for inserting/removing
66  connect(model, SIGNAL(layoutAboutToBeChanged()), this,
67  SLOT(layoutAboutToBeChanged()));
68  connect(model, SIGNAL(layoutChanged()), this, SLOT(layoutChanged()));
69 
70  connect(model, SIGNAL(rowsAboutToBeInserted(const QModelIndex&, int, int)),
71  this, SLOT(rowsAboutToBeInserted(const QModelIndex&, int, int)));
72  connect(model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex&, int, int)),
73  this, SLOT(rowsAboutToBeRemoved(const QModelIndex&, int, int)));
74  connect(model, SIGNAL(rowsInserted(const QModelIndex&, int, int)), this,
75  SLOT(rowsInserted(const QModelIndex&, int, int)));
76  connect(model, SIGNAL(rowsRemoved(const QModelIndex&, int, int)), this,
77  SLOT(rowsRemoved(const QModelIndex&, int, int)));
78 
79  runAllTests();
80 }
81 
83 {
84  if (fetchingMore)
85  return;
87  rowCount();
88  columnCount();
89  hasIndex();
90  index();
91  parent();
92  data();
93 }
94 
101 {
102  Q_ASSERT(model->buddy(QModelIndex()) == QModelIndex());
103  model->canFetchMore(QModelIndex());
104  Q_ASSERT(model->columnCount(QModelIndex()) >= 0);
105  Q_ASSERT(model->data(QModelIndex()) == QVariant());
106  fetchingMore = true;
107  model->fetchMore(QModelIndex());
108  fetchingMore = false;
109  Qt::ItemFlags flags = model->flags(QModelIndex());
110  Q_ASSERT(flags == Qt::ItemIsDropEnabled || flags == 0);
111  model->hasChildren(QModelIndex());
112  model->hasIndex(0, 0);
113  model->headerData(0, Qt::Horizontal);
114  model->index(0, 0);
115  Q_ASSERT(model->index(-1, -1) == QModelIndex());
116  model->itemData(QModelIndex());
117  QVariant cache;
118  model->match(QModelIndex(), -1, cache);
119  model->mimeTypes();
120  Q_ASSERT(model->parent(QModelIndex()) == QModelIndex());
121  Q_ASSERT(model->rowCount() >= 0);
122  QVariant variant;
123  model->setData(QModelIndex(), variant, -1);
124  model->setHeaderData(-1, Qt::Horizontal, QVariant());
125  model->setHeaderData(0, Qt::Horizontal, QVariant());
126  model->setHeaderData(999999, Qt::Horizontal, QVariant());
127  QMap<int, QVariant> roles;
128  model->sibling(0, 0, QModelIndex());
129  model->span(QModelIndex());
130  model->supportedDropActions();
131 }
132 
140 {
141  // check top row
142  QModelIndex topIndex = model->index(0, 0, QModelIndex());
143  int rows = model->rowCount(topIndex);
144  Q_ASSERT(rows >= 0);
145  if (rows > 0)
146  Q_ASSERT(model->hasChildren(topIndex) == true);
147 
148  QModelIndex secondLevelIndex = model->index(0, 0, topIndex);
149  if (secondLevelIndex.isValid()) // not the top level
150  { // check a row count where parent is valid
151  rows = model->rowCount(secondLevelIndex);
152  Q_ASSERT(rows >= 0);
153  if (rows > 0)
154  Q_ASSERT(model->hasChildren(secondLevelIndex) == true);
155  }
156 
157  // The models rowCount() is tested more extensively in checkChildren(),
158  // but this catches the big mistakes
159 }
160 
166 {
167  // check top row
168  QModelIndex topIndex = model->index(0, 0, QModelIndex());
169  Q_ASSERT(model->columnCount(topIndex) >= 0);
170 
171  // check a column count where parent is valid
172  QModelIndex childIndex = model->index(0, 0, topIndex);
173  if (childIndex.isValid())
174  Q_ASSERT(model->columnCount(childIndex) >= 0);
175 
176  // columnCount() is tested more extensively in checkChildren(),
177  // but this catches the big mistakes
178 }
179 
184 {
185  // Make sure that invalid values returns an invalid index
186  Q_ASSERT(model->hasIndex(-2, -2) == false);
187  Q_ASSERT(model->hasIndex(-2, 0) == false);
188  Q_ASSERT(model->hasIndex(0, -2) == false);
189 
190  int rows = model->rowCount();
191  int columns = model->columnCount();
192 
193  // check out of bounds
194  Q_ASSERT(model->hasIndex(rows, columns) == false);
195  Q_ASSERT(model->hasIndex(rows + 1, columns + 1) == false);
196 
197  if (rows > 0)
198  Q_ASSERT(model->hasIndex(0, 0) == true);
199 
200  // hasIndex() is tested more extensively in checkChildren(),
201  // but this catches the big mistakes
202 }
203 
208 {
209  // Make sure that invalid values returns an invalid index
210  Q_ASSERT(model->index(-2, -2) == QModelIndex());
211  Q_ASSERT(model->index(-2, 0) == QModelIndex());
212  Q_ASSERT(model->index(0, -2) == QModelIndex());
213 
214  int rows = model->rowCount();
215  int columns = model->columnCount();
216 
217  if (rows == 0)
218  return;
219 
220  // Catch off by one errors
221  Q_ASSERT(model->index(rows, columns) == QModelIndex());
222  Q_ASSERT(model->index(0, 0).isValid() == true);
223 
224  // Make sure that the same index is *always* returned
225  QModelIndex a = model->index(0, 0);
226  QModelIndex b = model->index(0, 0);
227  Q_ASSERT(a == b);
228 
229  // index() is tested more extensively in checkChildren(),
230  // but this catches the big mistakes
231 }
232 
237 {
238  // Make sure the model won't crash and will return an invalid QModelIndex
239  // when asked for the parent of an invalid index.
240  Q_ASSERT(model->parent(QModelIndex()) == QModelIndex());
241 
242  if (model->rowCount() == 0)
243  return;
244 
245  // Column 0 | Column 1 |
246  // QModelIndex() | |
247  // \- topIndex | topIndex1 |
248  // \- childIndex | childIndex1 |
249 
250  // Common error test #1, make sure that a top level index has a parent
251  // that is a invalid QModelIndex.
252  QModelIndex topIndex = model->index(0, 0, QModelIndex());
253  Q_ASSERT(model->parent(topIndex) == QModelIndex());
254 
255  // Common error test #2, make sure that a second level index has a parent
256  // that is the first level index.
257  if (model->rowCount(topIndex) > 0)
258  {
259  QModelIndex childIndex = model->index(0, 0, topIndex);
260  Q_ASSERT(model->parent(childIndex) == topIndex);
261  }
262 
263  // Common error test #3, the second column should NOT have the same children
264  // as the first column in a row.
265  // Usually the second column shouldn't have children.
266  QModelIndex topIndex1 = model->index(0, 1, QModelIndex());
267  if (model->rowCount(topIndex1) > 0)
268  {
269  QModelIndex childIndex = model->index(0, 0, topIndex);
270  QModelIndex childIndex1 = model->index(0, 0, topIndex1);
271  Q_ASSERT(childIndex != childIndex1);
272  }
273 
274  // Full test, walk n levels deep through the model making sure that all
275  // parent's children correctly specify their parent.
276  checkChildren(QModelIndex());
277 }
278 
293 void ModelTest::checkChildren(const QModelIndex& parent, int currentDepth)
294 {
295  // First just try walking back up the tree.
296  QModelIndex p = parent;
297  while (p.isValid())
298  p = p.parent();
299 
300  // For models that are dynamically populated
301  if (model->canFetchMore(parent))
302  {
303  fetchingMore = true;
304  model->fetchMore(parent);
305  fetchingMore = false;
306  }
307 
308  int rows = model->rowCount(parent);
309  int columns = model->columnCount(parent);
310 
311  if (rows > 0)
312  Q_ASSERT(model->hasChildren(parent));
313 
314  // Some further testing against rows(), columns(), and hasChildren()
315  Q_ASSERT(rows >= 0);
316  Q_ASSERT(columns >= 0);
317  if (rows > 0)
318  Q_ASSERT(model->hasChildren(parent) == true);
319 
320  // qDebug() << "parent:" << model->data(parent).toString() << "rows:" <<
321  // rows
322  // << "columns:" << columns << "parent column:" << parent.column();
323 
324  Q_ASSERT(model->hasIndex(rows + 1, 0, parent) == false);
325  for (int r = 0; r < rows; ++r)
326  {
327  if (model->canFetchMore(parent))
328  {
329  fetchingMore = true;
330  model->fetchMore(parent);
331  fetchingMore = false;
332  }
333  Q_ASSERT(model->hasIndex(r, columns + 1, parent) == false);
334  for (int c = 0; c < columns; ++c)
335  {
336  Q_ASSERT(model->hasIndex(r, c, parent) == true);
337  QModelIndex index = model->index(r, c, parent);
338  // rowCount() and columnCount() said that it existed...
339  Q_ASSERT(index.isValid() == true);
340 
341  // index() should always return the same index when called twice in
342  // a row
343  QModelIndex modifiedIndex = model->index(r, c, parent);
344  Q_ASSERT(index == modifiedIndex);
345 
346  // Make sure we get the same index if we request it twice in a row
347  QModelIndex a = model->index(r, c, parent);
348  QModelIndex b = model->index(r, c, parent);
349  Q_ASSERT(a == b);
350 
351  // Some basic checking on the index that is returned
352  Q_ASSERT(index.model() == model);
353  Q_ASSERT(index.row() == r);
354  Q_ASSERT(index.column() == c);
355  // While you can technically return a QVariant usually this is a
356  // sign of an bug in data() Disable if this really is ok in your
357  // model.
358  // Q_ASSERT(model->data(index, Qt::DisplayRole).isValid() == true);
359 
360  // If the next test fails here is some somewhat useful debug you
361  // play with.
362  /*
363  if (model->parent(index) != parent) {
364  qDebug() << r << c << currentDepth <<
365  model->data(index).toString()
366  << model->data(parent).toString();
367  qDebug() << index << parent << model->parent(index);
368  // And a view that you can even use to show the model.
369  //QTreeView view;
370  //view.setModel(model);
371  //view.show();
372  }*/
373 
374  // Check that we can get back our real parent.
375  QModelIndex p = model->parent(index);
376  // qDebug() << "child:" << index;
377  // qDebug() << p;
378  // qDebug() << parent;
379  Q_ASSERT(model->parent(index) == parent);
380 
381  // recursively go down the children
382  if (model->hasChildren(index) && currentDepth < 10)
383  // qDebug() << r << c << "has children" <<
384  // model->rowCount(index);
385  checkChildren(index, ++currentDepth);
386  /* else { if (currentDepth >= 10) qDebug() << "checked 10 deep";
387  * };*/
388 
389  // make sure that after testing the children that the index doesn't
390  // change.
391  QModelIndex newerIndex = model->index(r, c, parent);
392  Q_ASSERT(index == newerIndex);
393  }
394  }
395 }
396 
401 {
402  // Invalid index should return an invalid qvariant
403  Q_ASSERT(!model->data(QModelIndex()).isValid());
404 
405  if (model->rowCount() == 0)
406  return;
407 
408  // A valid index should have a valid QVariant data
409  Q_ASSERT(model->index(0, 0).isValid());
410 
411  // shouldn't be able to set data on an invalid index
412  Q_ASSERT(model->setData(QModelIndex(), QLatin1String("foo"),
413  Qt::DisplayRole) == false);
414 
415  // General Purpose roles that should return a QString
416  QVariant variant = model->data(model->index(0, 0), Qt::ToolTipRole);
417  if (variant.isValid())
418  Q_ASSERT(variant.canConvert(QMetaType::QString)));
419  variant = model->data(model->index(0, 0), Qt::StatusTipRole);
420  if (variant.isValid())
421  Q_ASSERT(variant.canConvert(QMetaType::QString)));
422  variant = model->data(model->index(0, 0), Qt::WhatsThisRole);
423  if (variant.isValid())
424  Q_ASSERT(variant.canConvert(QMetaType::QString)));
425 
426  // General Purpose roles that should return a QSize
427  variant = model->data(model->index(0, 0), Qt::SizeHintRole);
428  if (variant.isValid())
429  Q_ASSERT(variant.canConvert(QMetaType::QString)));
430 
431  // General Purpose roles that should return a QFont
432  QVariant fontVariant = model->data(model->index(0, 0), Qt::FontRole);
433  if (fontVariant.isValid())
434  Q_ASSERT(fontVariant.canConvert(QMetaType::QFont)));
435 
436  // Check that the alignment is one we know about
437  QVariant textAlignmentVariant =
438  model->data(model->index(0, 0), Qt::TextAlignmentRole);
439  if (textAlignmentVariant.isValid())
440  {
441  int alignment = textAlignmentVariant.toInt();
442  Q_ASSERT(
443  alignment == Qt::AlignLeft || alignment == Qt::AlignRight ||
444  alignment == Qt::AlignHCenter || alignment == Qt::AlignJustify ||
445  alignment == Qt::AlignTop || alignment == Qt::AlignBottom ||
446  alignment == Qt::AlignVCenter || alignment == Qt::AlignCenter ||
447  alignment == Qt::AlignAbsolute || alignment == Qt::AlignLeading ||
448  alignment == Qt::AlignTrailing);
449  }
450 
451  // General Purpose roles that should return a QColor
452  QVariant colorVariant =
453  model->data(model->index(0, 0), Qt::BackgroundColorRole);
454  if (colorVariant.isValid())
455  Q_ASSERT(colorVariant.canConvert(QMetaType::QColor)));
456 
457  colorVariant = model->data(model->index(0, 0), Qt::TextColorRole);
458  if (colorVariant.isValid())
459  Q_ASSERT(colorVariant.canConvert(QMetaType::QColor)));
460 
461  // Check that the "check state" is one we know about.
462  QVariant checkStateVariant =
463  model->data(model->index(0, 0), Qt::CheckStateRole);
464  if (checkStateVariant.isValid())
465  {
466  int state = checkStateVariant.toInt();
467  Q_ASSERT(state == Qt::Unchecked || state == Qt::PartiallyChecked ||
468  state == Qt::Checked);
469  }
470 }
471 
477 void ModelTest::rowsAboutToBeInserted(const QModelIndex& parent, int start,
478  int end)
479 {
480  Q_UNUSED(end);
481  Changing c;
482  c.parent = parent;
483  c.oldSize = model->rowCount(parent);
484  c.last = model->data(model->index(start - 1, 0, parent));
485  c.next = model->data(model->index(start, 0, parent));
486  insert.push(c);
487 }
488 
494 void ModelTest::rowsInserted(const QModelIndex& parent, int start, int end)
495 {
496  Changing c = insert.pop();
497  Q_ASSERT(c.parent == parent);
498  Q_ASSERT(c.oldSize + (end - start + 1) == model->rowCount(parent));
499  Q_ASSERT(c.last == model->data(model->index(start - 1, 0, c.parent)));
500  /*
501  if (c.next != model->data(model->index(end + 1, 0, c.parent))) {
502  qDebug() << start << end;
503  for (int i=0; i < model->rowCount(); ++i)
504  qDebug() << model->index(i, 0).data().toString();
505  qDebug() << c.next << model->data(model->index(end + 1, 0, c.parent));
506  }
507  */
508  Q_ASSERT(c.next == model->data(model->index(end + 1, 0, c.parent)));
509 }
510 
512 {
513  for (int i = 0; i < qBound(0, model->rowCount(), 100); ++i)
514  changing.append(QPersistentModelIndex(model->index(i, 0)));
515 }
516 
518 {
519  for (int i = 0; i < changing.count(); ++i)
520  {
521  QPersistentModelIndex p = changing[i];
522  Q_ASSERT(p == model->index(p.row(), p.column(), p.parent()));
523  }
524  changing.clear();
525 }
526 
532 void ModelTest::rowsAboutToBeRemoved(const QModelIndex& parent, int start,
533  int end)
534 {
535  Changing c;
536  c.parent = parent;
537  c.oldSize = model->rowCount(parent);
538  c.last = model->data(model->index(start - 1, 0, parent));
539  c.next = model->data(model->index(end + 1, 0, parent));
540  remove.push(c);
541 }
542 
548 void ModelTest::rowsRemoved(const QModelIndex& parent, int start, int end)
549 {
550  Changing c = remove.pop();
551  Q_ASSERT(c.parent == parent);
552  Q_ASSERT(c.oldSize - (end - start + 1) == model->rowCount(parent));
553  Q_ASSERT(c.last == model->data(model->index(start - 1, 0, c.parent)));
554  Q_ASSERT(c.next == model->data(model->index(start, 0, c.parent)));
555 }
void rowsAboutToBeInserted(const QModelIndex &parent, int start, int end)
Definition: modeltest.cpp:477
void hasIndex()
Definition: modeltest.cpp:183
void parent()
Definition: modeltest.cpp:236
bool fetchingMore
Definition: modeltest.h:70
void nonDestructiveBasicTest()
Definition: modeltest.cpp:100
QAbstractItemModel * model
Definition: modeltest.h:58
void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
Definition: modeltest.cpp:532
void index()
Definition: modeltest.cpp:207
void layoutChanged()
Definition: modeltest.cpp:517
void layoutAboutToBeChanged()
Definition: modeltest.cpp:511
void checkChildren(const QModelIndex &parent, int currentDepth=0)
Definition: modeltest.cpp:293
void runAllTests()
Definition: modeltest.cpp:82
void data()
Definition: modeltest.cpp:400
QList< QPersistentModelIndex > changing
Definition: modeltest.h:72
void rowsRemoved(const QModelIndex &parent, int start, int end)
Definition: modeltest.cpp:548
void rowCount()
Definition: modeltest.cpp:139
QStack< Changing > remove
Definition: modeltest.h:68
QStack< Changing > insert
Definition: modeltest.h:67
void columnCount()
Definition: modeltest.cpp:165
void rowsInserted(const QModelIndex &parent, int start, int end)
Definition: modeltest.cpp:494
static const double p
static const double r