1
/****************************************************************************
3
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4
** All rights reserved.
5
** Contact: Nokia Corporation (qt-info@nokia.com)
7
** This file is part of the test suite of the Qt Toolkit.
9
** $QT_BEGIN_LICENSE:LGPL$
10
** GNU Lesser General Public License Usage
11
** This file may be used under the terms of the GNU Lesser General Public
12
** License version 2.1 as published by the Free Software Foundation and
13
** appearing in the file LICENSE.LGPL included in the packaging of this
14
** file. Please review the following information to ensure the GNU Lesser
15
** General Public License version 2.1 requirements will be met:
16
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18
** In addition, as a special exception, Nokia gives you certain additional
19
** rights. These rights are described in the Nokia Qt LGPL Exception
20
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22
** GNU General Public License Usage
23
** Alternatively, this file may be used under the terms of the GNU General
24
** Public License version 3.0 as published by the Free Software Foundation
25
** and appearing in the file LICENSE.GPL included in the packaging of this
26
** file. Please review the following information to ensure the GNU General
27
** Public License version 3.0 requirements will be met:
28
** http://www.gnu.org/copyleft/gpl.html.
31
** Alternatively, this file may be used in accordance with the terms and
32
** conditions contained in a signed written agreement between you and Nokia.
40
****************************************************************************/
43
#include <QtGui/QtGui>
45
#include "modeltest.h"
47
#include <QtTest/QtTest>
49
#define Q_ASSERT QVERIFY
51
Q_DECLARE_METATYPE ( QModelIndex )
54
Connect to all of the models signals. Whenever anything happens recheck everything.
56
ModelTest::ModelTest ( QAbstractItemModel *_model, QObject *parent ) : QObject ( parent ), model ( _model ), fetchingMore ( false )
60
connect ( model, SIGNAL ( columnsAboutToBeInserted ( const QModelIndex &, int, int ) ),
61
this, SLOT ( runAllTests() ) );
62
connect ( model, SIGNAL ( columnsAboutToBeRemoved ( const QModelIndex &, int, int ) ),
63
this, SLOT ( runAllTests() ) );
64
connect ( model, SIGNAL ( columnsInserted ( const QModelIndex &, int, int ) ),
65
this, SLOT ( runAllTests() ) );
66
connect ( model, SIGNAL ( columnsRemoved ( const QModelIndex &, int, int ) ),
67
this, SLOT ( runAllTests() ) );
68
connect ( model, SIGNAL ( dataChanged ( const QModelIndex &, const QModelIndex & ) ),
69
this, SLOT ( runAllTests() ) );
70
connect ( model, SIGNAL ( headerDataChanged ( Qt::Orientation, int, int ) ),
71
this, SLOT ( runAllTests() ) );
72
connect ( model, SIGNAL ( layoutAboutToBeChanged () ), this, SLOT ( runAllTests() ) );
73
connect ( model, SIGNAL ( layoutChanged () ), this, SLOT ( runAllTests() ) );
74
connect ( model, SIGNAL ( modelReset () ), this, SLOT ( runAllTests() ) );
75
connect ( model, SIGNAL ( rowsAboutToBeInserted ( const QModelIndex &, int, int ) ),
76
this, SLOT ( runAllTests() ) );
77
connect ( model, SIGNAL ( rowsAboutToBeRemoved ( const QModelIndex &, int, int ) ),
78
this, SLOT ( runAllTests() ) );
79
connect ( model, SIGNAL ( rowsInserted ( const QModelIndex &, int, int ) ),
80
this, SLOT ( runAllTests() ) );
81
connect ( model, SIGNAL ( rowsRemoved ( const QModelIndex &, int, int ) ),
82
this, SLOT ( runAllTests() ) );
84
// Special checks for inserting/removing
85
connect ( model, SIGNAL ( layoutAboutToBeChanged() ),
86
this, SLOT ( layoutAboutToBeChanged() ) );
87
connect ( model, SIGNAL ( layoutChanged() ),
88
this, SLOT ( layoutChanged() ) );
90
connect ( model, SIGNAL ( rowsAboutToBeInserted ( const QModelIndex &, int, int ) ),
91
this, SLOT ( rowsAboutToBeInserted ( const QModelIndex &, int, int ) ) );
92
connect ( model, SIGNAL ( rowsAboutToBeRemoved ( const QModelIndex &, int, int ) ),
93
this, SLOT ( rowsAboutToBeRemoved ( const QModelIndex &, int, int ) ) );
94
connect ( model, SIGNAL ( rowsInserted ( const QModelIndex &, int, int ) ),
95
this, SLOT ( rowsInserted ( const QModelIndex &, int, int ) ) );
96
connect ( model, SIGNAL ( rowsRemoved ( const QModelIndex &, int, int ) ),
97
this, SLOT ( rowsRemoved ( const QModelIndex &, int, int ) ) );
102
void ModelTest::runAllTests()
106
nonDestructiveBasicTest();
116
nonDestructiveBasicTest tries to call a number of the basic functions (not all)
117
to make sure the model doesn't outright segfault, testing the functions that makes sense.
119
void ModelTest::nonDestructiveBasicTest()
121
Q_ASSERT ( model->buddy ( QModelIndex() ) == QModelIndex() );
122
model->canFetchMore ( QModelIndex() );
123
Q_ASSERT ( model->columnCount ( QModelIndex() ) >= 0 );
124
Q_ASSERT ( model->data ( QModelIndex() ) == QVariant() );
126
model->fetchMore ( QModelIndex() );
127
fetchingMore = false;
128
Qt::ItemFlags flags = model->flags ( QModelIndex() );
129
Q_ASSERT ( flags == Qt::ItemIsDropEnabled || flags == 0 );
130
model->hasChildren ( QModelIndex() );
131
model->hasIndex ( 0, 0 );
132
model->headerData ( 0, Qt::Horizontal );
133
model->index ( 0, 0 );
134
model->itemData ( QModelIndex() );
136
model->match ( QModelIndex(), -1, cache );
138
Q_ASSERT ( model->parent ( QModelIndex() ) == QModelIndex() );
139
Q_ASSERT ( model->rowCount() >= 0 );
141
model->setData ( QModelIndex(), variant, -1 );
142
model->setHeaderData ( -1, Qt::Horizontal, QVariant() );
143
model->setHeaderData ( 999999, Qt::Horizontal, QVariant() );
144
QMap<int, QVariant> roles;
145
model->sibling ( 0, 0, QModelIndex() );
146
model->span ( QModelIndex() );
147
model->supportedDropActions();
151
Tests model's implementation of QAbstractItemModel::rowCount() and hasChildren()
153
Models that are dynamically populated are not as fully tested here.
155
void ModelTest::rowCount()
159
QModelIndex topIndex = model->index ( 0, 0, QModelIndex() );
160
int rows = model->rowCount ( topIndex );
161
Q_ASSERT ( rows >= 0 );
163
Q_ASSERT ( model->hasChildren ( topIndex ) == true );
165
QModelIndex secondLevelIndex = model->index ( 0, 0, topIndex );
166
if ( secondLevelIndex.isValid() ) { // not the top level
167
// check a row count where parent is valid
168
rows = model->rowCount ( secondLevelIndex );
169
Q_ASSERT ( rows >= 0 );
171
Q_ASSERT ( model->hasChildren ( secondLevelIndex ) == true );
174
// The models rowCount() is tested more extensively in checkChildren(),
175
// but this catches the big mistakes
179
Tests model's implementation of QAbstractItemModel::columnCount() and hasChildren()
181
void ModelTest::columnCount()
184
QModelIndex topIndex = model->index ( 0, 0, QModelIndex() );
185
Q_ASSERT ( model->columnCount ( topIndex ) >= 0 );
187
// check a column count where parent is valid
188
QModelIndex childIndex = model->index ( 0, 0, topIndex );
189
if ( childIndex.isValid() )
190
Q_ASSERT ( model->columnCount ( childIndex ) >= 0 );
192
// columnCount() is tested more extensively in checkChildren(),
193
// but this catches the big mistakes
197
Tests model's implementation of QAbstractItemModel::hasIndex()
199
void ModelTest::hasIndex()
202
// Make sure that invalid values returns an invalid index
203
Q_ASSERT ( model->hasIndex ( -2, -2 ) == false );
204
Q_ASSERT ( model->hasIndex ( -2, 0 ) == false );
205
Q_ASSERT ( model->hasIndex ( 0, -2 ) == false );
207
int rows = model->rowCount();
208
int columns = model->columnCount();
210
// check out of bounds
211
Q_ASSERT ( model->hasIndex ( rows, columns ) == false );
212
Q_ASSERT ( model->hasIndex ( rows + 1, columns + 1 ) == false );
215
Q_ASSERT ( model->hasIndex ( 0, 0 ) == true );
217
// hasIndex() is tested more extensively in checkChildren(),
218
// but this catches the big mistakes
222
Tests model's implementation of QAbstractItemModel::index()
224
void ModelTest::index()
227
// Make sure that invalid values returns an invalid index
228
Q_ASSERT ( model->index ( -2, -2 ) == QModelIndex() );
229
Q_ASSERT ( model->index ( -2, 0 ) == QModelIndex() );
230
Q_ASSERT ( model->index ( 0, -2 ) == QModelIndex() );
232
int rows = model->rowCount();
233
int columns = model->columnCount();
238
// Catch off by one errors
239
Q_ASSERT ( model->index ( rows, columns ) == QModelIndex() );
240
Q_ASSERT ( model->index ( 0, 0 ).isValid() == true );
242
// Make sure that the same index is *always* returned
243
QModelIndex a = model->index ( 0, 0 );
244
QModelIndex b = model->index ( 0, 0 );
247
// index() is tested more extensively in checkChildren(),
248
// but this catches the big mistakes
252
Tests model's implementation of QAbstractItemModel::parent()
254
void ModelTest::parent()
257
// Make sure the model wont crash and will return an invalid QModelIndex
258
// when asked for the parent of an invalid index.
259
Q_ASSERT ( model->parent ( QModelIndex() ) == QModelIndex() );
261
if ( model->rowCount() == 0 )
264
// Column 0 | Column 1 |
266
// \- topIndex | topIndex1 |
267
// \- childIndex | childIndex1 |
269
// Common error test #1, make sure that a top level index has a parent
270
// that is a invalid QModelIndex.
271
QModelIndex topIndex = model->index ( 0, 0, QModelIndex() );
272
Q_ASSERT ( model->parent ( topIndex ) == QModelIndex() );
274
// Common error test #2, make sure that a second level index has a parent
275
// that is the first level index.
276
if ( model->rowCount ( topIndex ) > 0 ) {
277
QModelIndex childIndex = model->index ( 0, 0, topIndex );
278
Q_ASSERT ( model->parent ( childIndex ) == topIndex );
281
// Common error test #3, the second column should NOT have the same children
282
// as the first column in a row.
283
// Usually the second column shouldn't have children.
284
QModelIndex topIndex1 = model->index ( 0, 1, QModelIndex() );
285
if ( model->rowCount ( topIndex1 ) > 0 ) {
286
QModelIndex childIndex = model->index ( 0, 0, topIndex );
287
QModelIndex childIndex1 = model->index ( 0, 0, topIndex1 );
288
Q_ASSERT ( childIndex != childIndex1 );
291
// Full test, walk n levels deep through the model making sure that all
292
// parent's children correctly specify their parent.
293
checkChildren ( QModelIndex() );
297
Called from the parent() test.
299
A model that returns an index of parent X should also return X when asking
300
for the parent of the index.
302
This recursive function does pretty extensive testing on the whole model in an
303
effort to catch edge cases.
305
This function assumes that rowCount(), columnCount() and index() already work.
306
If they have a bug it will point it out, but the above tests should have already
307
found the basic bugs because it is easier to figure out the problem in
308
those tests then this one.
310
void ModelTest::checkChildren ( const QModelIndex &parent, int currentDepth )
312
// First just try walking back up the tree.
313
QModelIndex p = parent;
314
while ( p.isValid() )
317
// For models that are dynamically populated
318
if ( model->canFetchMore ( parent ) ) {
320
model->fetchMore ( parent );
321
fetchingMore = false;
324
int rows = model->rowCount ( parent );
325
int columns = model->columnCount ( parent );
328
Q_ASSERT ( model->hasChildren ( parent ) );
330
// Some further testing against rows(), columns(), and hasChildren()
331
Q_ASSERT ( rows >= 0 );
332
Q_ASSERT ( columns >= 0 );
334
Q_ASSERT ( model->hasChildren ( parent ) == true );
336
//qDebug() << "parent:" << model->data(parent).toString() << "rows:" << rows
337
// << "columns:" << columns << "parent column:" << parent.column();
339
Q_ASSERT ( model->hasIndex ( rows + 1, 0, parent ) == false );
340
for ( int r = 0; r < rows; ++r ) {
341
if ( model->canFetchMore ( parent ) ) {
343
model->fetchMore ( parent );
344
fetchingMore = false;
346
Q_ASSERT ( model->hasIndex ( r, columns + 1, parent ) == false );
347
for ( int c = 0; c < columns; ++c ) {
348
Q_ASSERT ( model->hasIndex ( r, c, parent ) == true );
349
QModelIndex index = model->index ( r, c, parent );
350
// rowCount() and columnCount() said that it existed...
351
Q_ASSERT ( index.isValid() == true );
353
// index() should always return the same index when called twice in a row
354
QModelIndex modifiedIndex = model->index ( r, c, parent );
355
Q_ASSERT ( index == modifiedIndex );
357
// Make sure we get the same index if we request it twice in a row
358
QModelIndex a = model->index ( r, c, parent );
359
QModelIndex b = model->index ( r, c, parent );
362
// Some basic checking on the index that is returned
363
Q_ASSERT ( index.model() == model );
364
Q_ASSERT ( index.row() == r );
365
Q_ASSERT ( index.column() == c );
366
// While you can technically return a QVariant usually this is a sign
367
// of an bug in data() Disable if this really is ok in your model.
368
// Q_ASSERT ( model->data ( index, Qt::DisplayRole ).isValid() == true );
370
// If the next test fails here is some somewhat useful debug you play with.
372
if (model->parent(index) != parent) {
373
qDebug() << r << c << currentDepth << model->data(index).toString()
374
<< model->data(parent).toString();
375
qDebug() << index << parent << model->parent(index);
376
// And a view that you can even use to show the model.
378
// view.setModel(model);
382
// Check that we can get back our real parent.
383
// qDebug() << model->parent ( index ) << parent ;
384
Q_ASSERT ( model->parent ( index ) == parent );
386
// recursively go down the children
387
if ( model->hasChildren ( index ) && currentDepth < 10 ) {
388
//qDebug() << r << c << "has children" << model->rowCount(index);
389
checkChildren ( index, ++currentDepth );
390
}/* else { if (currentDepth >= 10) qDebug() << "checked 10 deep"; };*/
392
// make sure that after testing the children that the index doesn't change.
393
QModelIndex newerIndex = model->index ( r, c, parent );
394
Q_ASSERT ( index == newerIndex );
400
Tests model's implementation of QAbstractItemModel::data()
402
void ModelTest::data()
404
// Invalid index should return an invalid qvariant
405
Q_ASSERT ( !model->data ( QModelIndex() ).isValid() );
407
if ( model->rowCount() == 0 )
410
// A valid index should have a valid QVariant data
411
Q_ASSERT ( model->index ( 0, 0 ).isValid() );
413
// shouldn't be able to set data on an invalid index
414
Q_ASSERT ( model->setData ( QModelIndex(), QLatin1String ( "foo" ), Qt::DisplayRole ) == false );
416
// General Purpose roles that should return a QString
417
QVariant variant = model->data ( model->index ( 0, 0 ), Qt::ToolTipRole );
418
if ( variant.isValid() ) {
419
Q_ASSERT ( qVariantCanConvert<QString> ( variant ) );
421
variant = model->data ( model->index ( 0, 0 ), Qt::StatusTipRole );
422
if ( variant.isValid() ) {
423
Q_ASSERT ( qVariantCanConvert<QString> ( variant ) );
425
variant = model->data ( model->index ( 0, 0 ), Qt::WhatsThisRole );
426
if ( variant.isValid() ) {
427
Q_ASSERT ( qVariantCanConvert<QString> ( variant ) );
430
// General Purpose roles that should return a QSize
431
variant = model->data ( model->index ( 0, 0 ), Qt::SizeHintRole );
432
if ( variant.isValid() ) {
433
Q_ASSERT ( qVariantCanConvert<QSize> ( variant ) );
436
// General Purpose roles that should return a QFont
437
QVariant fontVariant = model->data ( model->index ( 0, 0 ), Qt::FontRole );
438
if ( fontVariant.isValid() ) {
439
Q_ASSERT ( qVariantCanConvert<QFont> ( fontVariant ) );
442
// Check that the alignment is one we know about
443
QVariant textAlignmentVariant = model->data ( model->index ( 0, 0 ), Qt::TextAlignmentRole );
444
if ( textAlignmentVariant.isValid() ) {
445
int alignment = textAlignmentVariant.toInt();
446
Q_ASSERT ( alignment == ( alignment & ( Qt::AlignHorizontal_Mask | Qt::AlignVertical_Mask ) ) );
449
// General Purpose roles that should return a QColor
450
QVariant colorVariant = model->data ( model->index ( 0, 0 ), Qt::BackgroundColorRole );
451
if ( colorVariant.isValid() ) {
452
Q_ASSERT ( qVariantCanConvert<QColor> ( colorVariant ) );
455
colorVariant = model->data ( model->index ( 0, 0 ), Qt::TextColorRole );
456
if ( colorVariant.isValid() ) {
457
Q_ASSERT ( qVariantCanConvert<QColor> ( colorVariant ) );
460
// Check that the "check state" is one we know about.
461
QVariant checkStateVariant = model->data ( model->index ( 0, 0 ), Qt::CheckStateRole );
462
if ( checkStateVariant.isValid() ) {
463
int state = checkStateVariant.toInt();
464
Q_ASSERT ( state == Qt::Unchecked ||
465
state == Qt::PartiallyChecked ||
466
state == Qt::Checked );
471
Store what is about to be inserted to make sure it actually happens
475
void ModelTest::rowsAboutToBeInserted ( const QModelIndex &parent, int start, int end )
478
// qDebug() << "rowsAboutToBeInserted" << "start=" << start << "end=" << end << "parent=" << model->data ( parent ).toString()
479
// << "current count of parent=" << model->rowCount ( parent ); // << "display of last=" << model->data( model->index(start-1, 0, parent) );
480
// qDebug() << model->index(start-1, 0, parent) << model->data( model->index(start-1, 0, 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 ) );
490
Confirm that what was said was going to happen actually did
492
\sa rowsAboutToBeInserted()
494
void ModelTest::rowsInserted ( const QModelIndex & parent, int start, int end )
496
Changing c = insert.pop();
497
Q_ASSERT ( c.parent == parent );
498
// qDebug() << "rowsInserted" << "start=" << start << "end=" << end << "oldsize=" << c.oldSize
499
// << "parent=" << model->data ( parent ).toString() << "current rowcount of parent=" << model->rowCount ( parent );
501
// for (int ii=start; ii <= end; ii++)
503
// qDebug() << "itemWasInserted:" << ii << model->data ( model->index ( ii, 0, parent ));
507
Q_ASSERT ( c.oldSize + ( end - start + 1 ) == model->rowCount ( parent ) );
508
Q_ASSERT ( c.last == model->data ( model->index ( start - 1, 0, c.parent ) ) );
510
if (c.next != model->data(model->index(end + 1, 0, c.parent))) {
511
qDebug() << start << end;
512
for (int i=0; i < model->rowCount(); ++i)
513
qDebug() << model->index(i, 0).data().toString();
514
qDebug() << c.next << model->data(model->index(end + 1, 0, c.parent));
517
Q_ASSERT ( c.next == model->data ( model->index ( end + 1, 0, c.parent ) ) );
520
void ModelTest::layoutAboutToBeChanged()
522
for ( int i = 0; i < qBound ( 0, model->rowCount(), 100 ); ++i )
523
changing.append ( QPersistentModelIndex ( model->index ( i, 0 ) ) );
526
void ModelTest::layoutChanged()
528
for ( int i = 0; i < changing.count(); ++i ) {
529
QPersistentModelIndex p = changing[i];
530
Q_ASSERT ( p == model->index ( p.row(), p.column(), p.parent() ) );
536
Store what is about to be inserted to make sure it actually happens
540
void ModelTest::rowsAboutToBeRemoved ( const QModelIndex &parent, int start, int end )
542
qDebug() << "ratbr" << parent << start << end;
545
c.oldSize = model->rowCount ( parent );
546
c.last = model->data ( model->index ( start - 1, 0, parent ) );
547
c.next = model->data ( model->index ( end + 1, 0, parent ) );
552
Confirm that what was said was going to happen actually did
554
\sa rowsAboutToBeRemoved()
556
void ModelTest::rowsRemoved ( const QModelIndex & parent, int start, int end )
558
qDebug() << "rr" << parent << start << end;
559
Changing c = remove.pop();
560
Q_ASSERT ( c.parent == parent );
561
Q_ASSERT ( c.oldSize - ( end - start + 1 ) == model->rowCount ( parent ) );
562
Q_ASSERT ( c.last == model->data ( model->index ( start - 1, 0, c.parent ) ) );
563
Q_ASSERT ( c.next == model->data ( model->index ( start, 0, c.parent ) ) );