on 2492425 help sign in
unity8/tests/plugins/Utils/modeltest.cpp
604 lines 23 kB history raw
1/****************************************************************************
2** The full license is below, this is here to make licensecheck happy
3**
4** This program is free software; you can redistribute it and/or modify
5** it under the terms of the GNU Lesser General Public License as published by
6** the Free Software Foundation; version 2.1.
7**
8** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
9** Contact: http://www.qt-project.org/legal
10**
11** This file is part of the test suite of the Qt Toolkit.
12**
13** $QT_BEGIN_LICENSE:LGPL$
14** Commercial License Usage
15** Licensees holding valid commercial Qt licenses may use this file in
16** accordance with the commercial license agreement provided with the
17** Software or, alternatively, in accordance with the terms contained in
18** a written agreement between you and Digia. For licensing terms and
19** conditions see http://qt.digia.com/licensing. For further information
20** use the contact form at http://qt.digia.com/contact-us.
21**
22** GNU Lesser General Public License Usage
23** Alternatively, this file may be used under the terms of the GNU Lesser
24** General Public License version 2.1 as published by the Free Software
25** Foundation and appearing in the file LICENSE.LGPL included in the
26** packaging of this file. Please review the following information to
27** ensure the GNU Lesser General Public License version 2.1 requirements
28** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
29**
30** In addition, as a special exception, Digia gives you certain additional
31** rights. These rights are described in the Digia Qt LGPL Exception
32** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
33**
34** GNU General Public License Usage
35** Alternatively, this file may be used under the terms of the GNU
36** General Public License version 3.0 as published by the Free Software
37** Foundation and appearing in the file LICENSE.GPL included in the
38** packaging of this file. Please review the following information to
39** ensure the GNU General Public License version 3.0 requirements will be
40** met: http://www.gnu.org/copyleft/gpl.html.
41**
42**
43** $QT_END_LICENSE$
44**
45****************************************************************************/
48#include <QtGui/QtGui>
50#include "modeltest.h"
52#include <QtTest/QtTest>
54/*!
55 Connect to all of the models signals. Whenever anything happens recheck everything.
56*/
57ModelTest::ModelTest ( QAbstractItemModel *_model, QObject *parent ) : QObject ( parent ), model ( _model ), fetchingMore ( false )
59 if (!model)
60 qFatal("%s: model must not be null", Q_FUNC_INFO);
62 connect(model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)),
63 this, SLOT(runAllTests()) );
64 connect(model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
65 this, SLOT(runAllTests()) );
66 connect(model, SIGNAL(columnsInserted(QModelIndex,int,int)),
67 this, SLOT(runAllTests()) );
68 connect(model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
69 this, SLOT(runAllTests()) );
70 connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
71 this, SLOT(runAllTests()) );
72 connect(model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)),
73 this, SLOT(runAllTests()) );
74 connect(model, SIGNAL(layoutAboutToBeChanged()), this, SLOT(runAllTests()) );
75 connect(model, SIGNAL(layoutChanged()), this, SLOT(runAllTests()) );
76 connect(model, SIGNAL(modelReset()), this, SLOT(runAllTests()) );
77 connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
78 this, SLOT(runAllTests()) );
79 connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
80 this, SLOT(runAllTests()) );
81 connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)),
82 this, SLOT(runAllTests()) );
83 connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
84 this, SLOT(runAllTests()) );
86 // Special checks for changes
87 connect(model, SIGNAL(layoutAboutToBeChanged()),
88 this, SLOT(layoutAboutToBeChanged()) );
89 connect(model, SIGNAL(layoutChanged()),
90 this, SLOT(layoutChanged()) );
92 connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
93 this, SLOT(rowsAboutToBeInserted(QModelIndex,int,int)) );
94 connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
95 this, SLOT(rowsAboutToBeRemoved(QModelIndex,int,int)) );
96 connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)),
97 this, SLOT(rowsInserted(QModelIndex,int,int)) );
98 connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
99 this, SLOT(rowsRemoved(QModelIndex,int,int)) );
100 connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
101 this, SLOT(dataChanged(QModelIndex,QModelIndex)) );
102 connect(model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)),
103 this, SLOT(headerDataChanged(Qt::Orientation,int,int)) );
105 runAllTests();
108void ModelTest::runAllTests()
110 if ( fetchingMore )
111 return;
112 nonDestructiveBasicTest();
113 rowCount();
114 columnCount();
115 hasIndex();
116 index();
117 parent();
118 data();
121/*!
122 nonDestructiveBasicTest tries to call a number of the basic functions (not all)
123 to make sure the model doesn't outright segfault, testing the functions that makes sense.
124*/
125void ModelTest::nonDestructiveBasicTest()
127 QVERIFY( model->buddy ( QModelIndex() ) == QModelIndex() );
128 model->canFetchMore ( QModelIndex() );
129 QVERIFY( model->columnCount ( QModelIndex() ) >= 0 );
130 QVERIFY( model->data ( QModelIndex() ) == QVariant() );
131 fetchingMore = true;
132 model->fetchMore ( QModelIndex() );
133 fetchingMore = false;
134 Qt::ItemFlags flags = model->flags ( QModelIndex() );
135 QVERIFY( flags == Qt::ItemIsDropEnabled || flags == 0 );
136 model->hasChildren ( QModelIndex() );
137 model->hasIndex ( 0, 0 );
138 model->headerData ( 0, Qt::Horizontal );
139 model->index ( 0, 0 );
140 model->itemData ( QModelIndex() );
141 QVariant cache;
142 model->match ( QModelIndex(), -1, cache );
143 model->mimeTypes();
144 QVERIFY( model->parent ( QModelIndex() ) == QModelIndex() );
145 QVERIFY( model->rowCount() >= 0 );
146 QVariant variant;
147 model->setData ( QModelIndex(), variant, -1 );
148 model->setHeaderData ( -1, Qt::Horizontal, QVariant() );
149 model->setHeaderData ( 999999, Qt::Horizontal, QVariant() );
150 QMap<int, QVariant> roles;
151 model->sibling ( 0, 0, QModelIndex() );
152 model->span ( QModelIndex() );
153 model->supportedDropActions();
156/*!
157 Tests model's implementation of QAbstractItemModel::rowCount() and hasChildren()
159 Models that are dynamically populated are not as fully tested here.
160 */
161void ModelTest::rowCount()
163// qDebug() << "rc";
164 // check top row
165 QModelIndex topIndex = model->index ( 0, 0, QModelIndex() );
166 int rows = model->rowCount ( topIndex );
167 QVERIFY( rows >= 0 );
168 if ( rows > 0 )
169 QVERIFY( model->hasChildren ( topIndex ) );
171 QModelIndex secondLevelIndex = model->index ( 0, 0, topIndex );
172 if ( secondLevelIndex.isValid() ) { // not the top level
173 // check a row count where parent is valid
174 rows = model->rowCount ( secondLevelIndex );
175 QVERIFY( rows >= 0 );
176 if ( rows > 0 )
177 QVERIFY( model->hasChildren ( secondLevelIndex ) );
178 }
180 // The models rowCount() is tested more extensively in checkChildren(),
181 // but this catches the big mistakes
184/*!
185 Tests model's implementation of QAbstractItemModel::columnCount() and hasChildren()
186 */
187void ModelTest::columnCount()
189 // check top row
190 QModelIndex topIndex = model->index ( 0, 0, QModelIndex() );
191 QVERIFY( model->columnCount ( topIndex ) >= 0 );
193 // check a column count where parent is valid
194 QModelIndex childIndex = model->index ( 0, 0, topIndex );
195 if ( childIndex.isValid() )
196 QVERIFY( model->columnCount ( childIndex ) >= 0 );
198 // columnCount() is tested more extensively in checkChildren(),
199 // but this catches the big mistakes
202/*!
203 Tests model's implementation of QAbstractItemModel::hasIndex()
204 */
205void ModelTest::hasIndex()
207// qDebug() << "hi";
208 // Make sure that invalid values returns an invalid index
209 QVERIFY( !model->hasIndex ( -2, -2 ) );
210 QVERIFY( !model->hasIndex ( -2, 0 ) );
211 QVERIFY( !model->hasIndex ( 0, -2 ) );
213 int rows = model->rowCount();
214 int columns = model->columnCount();
216 // check out of bounds
217 QVERIFY( !model->hasIndex ( rows, columns ) );
218 QVERIFY( !model->hasIndex ( rows + 1, columns + 1 ) );
220 if ( rows > 0 )
221 QVERIFY( model->hasIndex ( 0, 0 ) );
223 // hasIndex() is tested more extensively in checkChildren(),
224 // but this catches the big mistakes
227/*!
228 Tests model's implementation of QAbstractItemModel::index()
229 */
230void ModelTest::index()
232// qDebug() << "i";
233 // Make sure that invalid values returns an invalid index
234 QVERIFY( model->index ( -2, -2 ) == QModelIndex() );
235 QVERIFY( model->index ( -2, 0 ) == QModelIndex() );
236 QVERIFY( model->index ( 0, -2 ) == QModelIndex() );
238 int rows = model->rowCount();
239 int columns = model->columnCount();
241 if ( rows == 0 )
242 return;
244 // Catch off by one errors
245 QVERIFY( model->index ( rows, columns ) == QModelIndex() );
246 QVERIFY( model->index ( 0, 0 ).isValid() );
248 // Make sure that the same index is *always* returned
249 QModelIndex a = model->index ( 0, 0 );
250 QModelIndex b = model->index ( 0, 0 );
251 QVERIFY( a == b );
253 // index() is tested more extensively in checkChildren(),
254 // but this catches the big mistakes
257/*!
258 Tests model's implementation of QAbstractItemModel::parent()
259 */
260void ModelTest::parent()
262// qDebug() << "p";
263 // Make sure the model won't crash and will return an invalid QModelIndex
264 // when asked for the parent of an invalid index.
265 QVERIFY( model->parent ( QModelIndex() ) == QModelIndex() );
267 if ( model->rowCount() == 0 )
268 return;
270 // Column 0 | Column 1 |
271 // QModelIndex() | |
272 // \- topIndex | topIndex1 |
273 // \- childIndex | childIndex1 |
275 // Common error test #1, make sure that a top level index has a parent
276 // that is a invalid QModelIndex.
277 QModelIndex topIndex = model->index ( 0, 0, QModelIndex() );
278 QVERIFY( model->parent ( topIndex ) == QModelIndex() );
280 // Common error test #2, make sure that a second level index has a parent
281 // that is the first level index.
282 if ( model->rowCount ( topIndex ) > 0 ) {
283 QModelIndex childIndex = model->index ( 0, 0, topIndex );
284 QVERIFY( model->parent ( childIndex ) == topIndex );
285 }
287 // Common error test #3, the second column should NOT have the same children
288 // as the first column in a row.
289 // Usually the second column shouldn't have children.
290 QModelIndex topIndex1 = model->index ( 0, 1, QModelIndex() );
291 if ( model->rowCount ( topIndex1 ) > 0 ) {
292 QModelIndex childIndex = model->index ( 0, 0, topIndex );
293 QModelIndex childIndex1 = model->index ( 0, 0, topIndex1 );
294 QVERIFY( childIndex != childIndex1 );
295 }
297 // Full test, walk n levels deep through the model making sure that all
298 // parent's children correctly specify their parent.
299 checkChildren ( QModelIndex() );
302/*!
303 Called from the parent() test.
305 A model that returns an index of parent X should also return X when asking
306 for the parent of the index.
308 This recursive function does pretty extensive testing on the whole model in an
309 effort to catch edge cases.
311 This function assumes that rowCount(), columnCount() and index() already work.
312 If they have a bug it will point it out, but the above tests should have already
313 found the basic bugs because it is easier to figure out the problem in
314 those tests then this one.
315 */
316void ModelTest::checkChildren ( const QModelIndex &parent, int currentDepth )
318 // First just try walking back up the tree.
319 QModelIndex p = parent;
320 while ( p.isValid() )
321 p = p.parent();
323 // For models that are dynamically populated
324 if ( model->canFetchMore ( parent ) ) {
325 fetchingMore = true;
326 model->fetchMore ( parent );
327 fetchingMore = false;
328 }
330 int rows = model->rowCount ( parent );
331 int columns = model->columnCount ( parent );
333 if ( rows > 0 )
334 QVERIFY( model->hasChildren ( parent ) );
336 // Some further testing against rows(), columns(), and hasChildren()
337 QVERIFY( rows >= 0 );
338 QVERIFY( columns >= 0 );
339 if ( rows > 0 )
340 QVERIFY( model->hasChildren ( parent ) );
342 //qDebug() << "parent:" << model->data(parent).toString() << "rows:" << rows
343 // << "columns:" << columns << "parent column:" << parent.column();
345 const QModelIndex topLeftChild = model->index( 0, 0, parent );
347 QVERIFY( !model->hasIndex ( rows + 1, 0, parent ) );
348 for ( int r = 0; r < rows; ++r ) {
349 if ( model->canFetchMore ( parent ) ) {
350 fetchingMore = true;
351 model->fetchMore ( parent );
352 fetchingMore = false;
353 }
354 QVERIFY( !model->hasIndex ( r, columns + 1, parent ) );
355 for ( int c = 0; c < columns; ++c ) {
356 QVERIFY( model->hasIndex ( r, c, parent ) );
357 QModelIndex index = model->index ( r, c, parent );
358 // rowCount() and columnCount() said that it existed...
359 QVERIFY( index.isValid() );
361 // index() should always return the same index when called twice in a row
362 QModelIndex modifiedIndex = model->index ( r, c, parent );
363 QVERIFY( index == modifiedIndex );
365 // Make sure we get the same index if we request it twice in a row
366 QModelIndex a = model->index ( r, c, parent );
367 QModelIndex b = model->index ( r, c, parent );
368 QVERIFY( a == b );
370 {
371 const QModelIndex sibling = model->sibling( r, c, topLeftChild );
372 QVERIFY( index == sibling );
373 }
374 {
375 const QModelIndex sibling = topLeftChild.sibling( r, c );
376 QVERIFY( index == sibling );
377 }
379 // Some basic checking on the index that is returned
380 QVERIFY( index.model() == model );
381 QCOMPARE( index.row(), r );
382 QCOMPARE( index.column(), c );
383 // While you can technically return a QVariant usually this is a sign
384 // of a bug in data(). Disable if this really is ok in your model.
385// QVERIFY( model->data ( index, Qt::DisplayRole ).isValid() );
387 // If the next test fails here is some somewhat useful debug you play with.
389 if (model->parent(index) != parent) {
390 qDebug() << r << c << currentDepth << model->data(index).toString()
391 << model->data(parent).toString();
392 qDebug() << index << parent << model->parent(index);
393// And a view that you can even use to show the model.
394// QTreeView view;
395// view.setModel(model);
396// view.show();
397 }
399 // Check that we can get back our real parent.
400 QCOMPARE( model->parent ( index ), parent );
402 // recursively go down the children
403 if ( model->hasChildren ( index ) && currentDepth < 10 ) {
404 //qDebug() << r << c << "has children" << model->rowCount(index);
405 checkChildren ( index, ++currentDepth );
406 }/* else { if (currentDepth >= 10) qDebug() << "checked 10 deep"; };*/
408 // make sure that after testing the children that the index doesn't change.
409 QModelIndex newerIndex = model->index ( r, c, parent );
410 QVERIFY( index == newerIndex );
411 }
412 }
415/*!
416 Tests model's implementation of QAbstractItemModel::data()
417 */
418void ModelTest::data()
420 // Invalid index should return an invalid qvariant
421 QVERIFY( !model->data ( QModelIndex() ).isValid() );
423 if ( model->rowCount() == 0 )
424 return;
426 // A valid index should have a valid QVariant data
427 QVERIFY( model->index ( 0, 0 ).isValid() );
429 // shouldn't be able to set data on an invalid index
430 QVERIFY( !model->setData ( QModelIndex(), QLatin1String ( "foo" ), Qt::DisplayRole ) );
432 // General Purpose roles that should return a QString
433 QVariant variant = model->data ( model->index ( 0, 0 ), Qt::ToolTipRole );
434 if ( variant.isValid() ) {
435 QVERIFY( variant.canConvert<QString>() );
436 }
437 variant = model->data ( model->index ( 0, 0 ), Qt::StatusTipRole );
438 if ( variant.isValid() ) {
439 QVERIFY( variant.canConvert<QString>() );
440 }
441 variant = model->data ( model->index ( 0, 0 ), Qt::WhatsThisRole );
442 if ( variant.isValid() ) {
443 QVERIFY( variant.canConvert<QString>() );
444 }
446 // General Purpose roles that should return a QSize
447 variant = model->data ( model->index ( 0, 0 ), Qt::SizeHintRole );
448 if ( variant.isValid() ) {
449 QVERIFY( variant.canConvert<QSize>() );
450 }
452 // General Purpose roles that should return a QFont
453 QVariant fontVariant = model->data ( model->index ( 0, 0 ), Qt::FontRole );
454 if ( fontVariant.isValid() ) {
455 QVERIFY( fontVariant.canConvert<QFont>() );
456 }
458 // Check that the alignment is one we know about
459 QVariant textAlignmentVariant = model->data ( model->index ( 0, 0 ), Qt::TextAlignmentRole );
460 if ( textAlignmentVariant.isValid() ) {
461 int alignment = textAlignmentVariant.toInt();
462 QCOMPARE( alignment, ( alignment & ( Qt::AlignHorizontal_Mask | Qt::AlignVertical_Mask ) ) );
463 }
465 // General Purpose roles that should return a QColor
466 QVariant colorVariant = model->data ( model->index ( 0, 0 ), Qt::BackgroundColorRole );
467 if ( colorVariant.isValid() ) {
468 QVERIFY( colorVariant.canConvert<QColor>() );
469 }
471 colorVariant = model->data ( model->index ( 0, 0 ), Qt::TextColorRole );
472 if ( colorVariant.isValid() ) {
473 QVERIFY( colorVariant.canConvert<QColor>() );
474 }
476 // Check that the "check state" is one we know about.
477 QVariant checkStateVariant = model->data ( model->index ( 0, 0 ), Qt::CheckStateRole );
478 if ( checkStateVariant.isValid() ) {
479 int state = checkStateVariant.toInt();
480 QVERIFY( state == Qt::Unchecked ||
481 state == Qt::PartiallyChecked ||
482 state == Qt::Checked );
483 }
486/*!
487 Store what is about to be inserted to make sure it actually happens
489 \sa rowsInserted()
490 */
491void ModelTest::rowsAboutToBeInserted ( const QModelIndex &parent, int start, int /* end */)
493// Q_UNUSED(end);
494// qDebug() << "rowsAboutToBeInserted" << "start=" << start << "end=" << end << "parent=" << model->data ( parent ).toString()
495// << "current count of parent=" << model->rowCount ( parent ); // << "display of last=" << model->data( model->index(start-1, 0, parent) );
496// qDebug() << model->index(start-1, 0, parent) << model->data( model->index(start-1, 0, parent) );
497 Changing c;
498 c.parent = parent;
499 c.oldSize = model->rowCount ( parent );
500 c.last = model->data ( model->index ( start - 1, 0, parent ) );
501 c.next = model->data ( model->index ( start, 0, parent ) );
502 insert.push ( c );
505/*!
506 Confirm that what was said was going to happen actually did
508 \sa rowsAboutToBeInserted()
509 */
510void ModelTest::rowsInserted ( const QModelIndex & parent, int start, int end )
512 Changing c = insert.pop();
513 QVERIFY( c.parent == parent );
514// qDebug() << "rowsInserted" << "start=" << start << "end=" << end << "oldsize=" << c.oldSize
515// << "parent=" << model->data ( parent ).toString() << "current rowcount of parent=" << model->rowCount ( parent );
517// for (int ii=start; ii <= end; ii++)
518// {
519// qDebug() << "itemWasInserted:" << ii << model->data ( model->index ( ii, 0, parent ));
520// }
521// qDebug();
523 QVERIFY( c.oldSize + ( end - start + 1 ) == model->rowCount ( parent ) );
524 QVERIFY( c.last == model->data ( model->index ( start - 1, 0, c.parent ) ) );
526 if (c.next != model->data(model->index(end + 1, 0, c.parent))) {
527 qDebug() << start << end;
528 for (int i=0; i < model->rowCount(); ++i)
529 qDebug() << model->index(i, 0).data().toString();
530 qDebug() << c.next << model->data(model->index(end + 1, 0, c.parent));
531 }
533 QVERIFY( c.next == model->data ( model->index ( end + 1, 0, c.parent ) ) );
536void ModelTest::layoutAboutToBeChanged()
538 for ( int i = 0; i < qBound ( 0, model->rowCount(), 100 ); ++i )
539 changing.append ( QPersistentModelIndex ( model->index ( i, 0 ) ) );
542void ModelTest::layoutChanged()
544 for ( int i = 0; i < changing.count(); ++i ) {
545 QPersistentModelIndex p = changing[i];
546 QVERIFY( p == model->index ( p.row(), p.column(), p.parent() ) );
547 }
548 changing.clear();
551/*!
552 Store what is about to be inserted to make sure it actually happens
554 \sa rowsRemoved()
555 */
556void ModelTest::rowsAboutToBeRemoved ( const QModelIndex &parent, int start, int end )
558qDebug() << "ratbr" << parent << start << end;
559 Changing c;
560 c.parent = parent;
561 c.oldSize = model->rowCount ( parent );
562 c.last = model->data ( model->index ( start - 1, 0, parent ) );
563 c.next = model->data ( model->index ( end + 1, 0, parent ) );
564 remove.push ( c );
567/*!
568 Confirm that what was said was going to happen actually did
570 \sa rowsAboutToBeRemoved()
571 */
572void ModelTest::rowsRemoved ( const QModelIndex & parent, int start, int end )
574 qDebug() << "rr" << parent << start << end;
575 Changing c = remove.pop();
576 QVERIFY( c.parent == parent );
577 QVERIFY( c.oldSize - ( end - start + 1 ) == model->rowCount ( parent ) );
578 QVERIFY( c.last == model->data ( model->index ( start - 1, 0, c.parent ) ) );
579 QVERIFY( c.next == model->data ( model->index ( start, 0, c.parent ) ) );
582void ModelTest::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
584 QVERIFY(topLeft.isValid());
585 QVERIFY(bottomRight.isValid());
586 QModelIndex commonParent = bottomRight.parent();
587 QVERIFY(topLeft.parent() == commonParent);
588 QVERIFY(topLeft.row() <= bottomRight.row());
589 QVERIFY(topLeft.column() <= bottomRight.column());
590 int rowCount = model->rowCount(commonParent);
591 int columnCount = model->columnCount(commonParent);
592 QVERIFY(bottomRight.row() < rowCount);
593 QVERIFY(bottomRight.column() < columnCount);
596void ModelTest::headerDataChanged(Qt::Orientation orientation, int start, int end)
598 QVERIFY(start >= 0);
599 QVERIFY(end >= 0);
600 QVERIFY(start <= end);
601 int itemCount = orientation == Qt::Vertical ? model->rowCount() : model->columnCount();
602 QVERIFY(start < itemCount);
603 QVERIFY(end < itemCount);