1
/****************************************************************************
2
** The full license is below, this is here to make licensecheck happy
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.
8
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
9
** Contact: http://www.qt-project.org/legal
11
** This file is part of the test suite of the Qt Toolkit.
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.
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.
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.
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.
45
****************************************************************************/
48
#include <QtGui/QtGui>
50
#include "modeltest.h"
52
#include <QtTest/QtTest>
55
Connect to all of the models signals. Whenever anything happens recheck everything.
57
ModelTest::ModelTest ( QAbstractItemModel *_model, QObject *parent ) : QObject ( parent ), model ( _model ), fetchingMore ( false )
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)) );
108
void ModelTest::runAllTests()
112
nonDestructiveBasicTest();
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.
125
void ModelTest::nonDestructiveBasicTest()
127
QVERIFY( model->buddy ( QModelIndex() ) == QModelIndex() );
128
model->canFetchMore ( QModelIndex() );
129
QVERIFY( model->columnCount ( QModelIndex() ) >= 0 );
130
QVERIFY( model->data ( QModelIndex() ) == QVariant() );
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() );
142
model->match ( QModelIndex(), -1, cache );
144
QVERIFY( model->parent ( QModelIndex() ) == QModelIndex() );
145
QVERIFY( model->rowCount() >= 0 );
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();
157
Tests model's implementation of QAbstractItemModel::rowCount() and hasChildren()
159
Models that are dynamically populated are not as fully tested here.
161
void ModelTest::rowCount()
165
QModelIndex topIndex = model->index ( 0, 0, QModelIndex() );
166
int rows = model->rowCount ( topIndex );
167
QVERIFY( 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 );
177
QVERIFY( model->hasChildren ( secondLevelIndex ) );
180
// The models rowCount() is tested more extensively in checkChildren(),
181
// but this catches the big mistakes
185
Tests model's implementation of QAbstractItemModel::columnCount() and hasChildren()
187
void ModelTest::columnCount()
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
203
Tests model's implementation of QAbstractItemModel::hasIndex()
205
void ModelTest::hasIndex()
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 ) );
221
QVERIFY( model->hasIndex ( 0, 0 ) );
223
// hasIndex() is tested more extensively in checkChildren(),
224
// but this catches the big mistakes
228
Tests model's implementation of QAbstractItemModel::index()
230
void ModelTest::index()
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();
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 );
253
// index() is tested more extensively in checkChildren(),
254
// but this catches the big mistakes
258
Tests model's implementation of QAbstractItemModel::parent()
260
void ModelTest::parent()
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 )
270
// Column 0 | Column 1 |
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 );
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 );
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() );
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.
316
void ModelTest::checkChildren ( const QModelIndex &parent, int currentDepth )
318
// First just try walking back up the tree.
319
QModelIndex p = parent;
320
while ( p.isValid() )
323
// For models that are dynamically populated
324
if ( model->canFetchMore ( parent ) ) {
326
model->fetchMore ( parent );
327
fetchingMore = false;
330
int rows = model->rowCount ( parent );
331
int columns = model->columnCount ( parent );
334
QVERIFY( model->hasChildren ( parent ) );
336
// Some further testing against rows(), columns(), and hasChildren()
337
QVERIFY( rows >= 0 );
338
QVERIFY( columns >= 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 ) ) {
351
model->fetchMore ( parent );
352
fetchingMore = false;
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 );
371
const QModelIndex sibling = model->sibling( r, c, topLeftChild );
372
QVERIFY( index == sibling );
375
const QModelIndex sibling = topLeftChild.sibling( r, c );
376
QVERIFY( index == sibling );
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.
395
// view.setModel(model);
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 );
416
Tests model's implementation of QAbstractItemModel::data()
418
void ModelTest::data()
420
// Invalid index should return an invalid qvariant
421
QVERIFY( !model->data ( QModelIndex() ).isValid() );
423
if ( model->rowCount() == 0 )
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>() );
437
variant = model->data ( model->index ( 0, 0 ), Qt::StatusTipRole );
438
if ( variant.isValid() ) {
439
QVERIFY( variant.canConvert<QString>() );
441
variant = model->data ( model->index ( 0, 0 ), Qt::WhatsThisRole );
442
if ( variant.isValid() ) {
443
QVERIFY( variant.canConvert<QString>() );
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>() );
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>() );
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 ) ) );
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>() );
471
colorVariant = model->data ( model->index ( 0, 0 ), Qt::TextColorRole );
472
if ( colorVariant.isValid() ) {
473
QVERIFY( colorVariant.canConvert<QColor>() );
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 );
487
Store what is about to be inserted to make sure it actually happens
491
void ModelTest::rowsAboutToBeInserted ( const QModelIndex &parent, int start, int /* 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) );
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 ) );
506
Confirm that what was said was going to happen actually did
508
\sa rowsAboutToBeInserted()
510
void 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++)
519
// qDebug() << "itemWasInserted:" << ii << model->data ( model->index ( ii, 0, parent ));
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));
533
QVERIFY( c.next == model->data ( model->index ( end + 1, 0, c.parent ) ) );
536
void ModelTest::layoutAboutToBeChanged()
538
for ( int i = 0; i < qBound ( 0, model->rowCount(), 100 ); ++i )
539
changing.append ( QPersistentModelIndex ( model->index ( i, 0 ) ) );
542
void 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() ) );
552
Store what is about to be inserted to make sure it actually happens
556
void ModelTest::rowsAboutToBeRemoved ( const QModelIndex &parent, int start, int end )
558
qDebug() << "ratbr" << parent << start << end;
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 ) );
568
Confirm that what was said was going to happen actually did
570
\sa rowsAboutToBeRemoved()
572
void 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 ) ) );
582
void 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);
596
void ModelTest::headerDataChanged(Qt::Orientation orientation, int start, int end)
600
QVERIFY(start <= end);
601
int itemCount = orientation == Qt::Vertical ? model->rowCount() : model->columnCount();
602
QVERIFY(start < itemCount);
603
QVERIFY(end < itemCount);