on master help sign in
unity8/tests/plugins/Utils/ModelTest.cpp
586 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, &QAbstractItemModel::columnsAboutToBeInserted, this, &ModelTest::runAllTests);
63 connect(model, &QAbstractItemModel::columnsAboutToBeRemoved, this, &ModelTest::runAllTests);
64 connect(model, &QAbstractItemModel::columnsInserted, this, &ModelTest::runAllTests);
65 connect(model, &QAbstractItemModel::columnsRemoved, this, &ModelTest::runAllTests);
66 connect(model, &QAbstractItemModel::dataChanged, this, &ModelTest::runAllTests);
67 connect(model, &QAbstractItemModel::headerDataChanged, this, &ModelTest::runAllTests);
68 connect(model, &QAbstractItemModel::layoutAboutToBeChanged, this, &ModelTest::runAllTests);
69 connect(model, &QAbstractItemModel::layoutChanged, this, &ModelTest::runAllTests);
70 connect(model, &QAbstractItemModel::modelReset, this, &ModelTest::runAllTests);
71 connect(model, &QAbstractItemModel::rowsAboutToBeInserted, this, &ModelTest::runAllTests);
72 connect(model, &QAbstractItemModel::rowsAboutToBeRemoved, this, &ModelTest::runAllTests);
73 connect(model, &QAbstractItemModel::rowsInserted, this, &ModelTest::runAllTests);
74 connect(model, &QAbstractItemModel::rowsRemoved, this, &ModelTest::runAllTests);
76 // Special checks for changes
77 connect(model, &QAbstractItemModel::layoutAboutToBeChanged, this, &ModelTest::layoutAboutToBeChanged);
78 connect(model, &QAbstractItemModel::layoutChanged, this, &ModelTest::layoutChanged);
80 connect(model, &QAbstractItemModel::rowsAboutToBeInserted, this, &ModelTest::rowsAboutToBeInserted);
81 connect(model, &QAbstractItemModel::rowsAboutToBeRemoved, this, &ModelTest::rowsAboutToBeRemoved);
82 connect(model, &QAbstractItemModel::rowsInserted, this, &ModelTest::rowsInserted);
83 connect(model, &QAbstractItemModel::rowsRemoved, this, &ModelTest::rowsRemoved);
84 connect(model, &QAbstractItemModel::dataChanged, this, &ModelTest::dataChanged);
85 connect(model, &QAbstractItemModel::headerDataChanged, this, &ModelTest::headerDataChanged);
87 runAllTests();
90void ModelTest::runAllTests()
92 if ( fetchingMore )
93 return;
94 nonDestructiveBasicTest();
95 rowCount();
96 columnCount();
97 hasIndex();
98 index();
99 parent();
100 data();
103/*!
104 nonDestructiveBasicTest tries to call a number of the basic functions (not all)
105 to make sure the model doesn't outright segfault, testing the functions that makes sense.
106*/
107void ModelTest::nonDestructiveBasicTest()
109 QVERIFY( model->buddy ( QModelIndex() ) == QModelIndex() );
110 model->canFetchMore ( QModelIndex() );
111 QVERIFY( model->columnCount ( QModelIndex() ) >= 0 );
112 QVERIFY( model->data ( QModelIndex() ) == QVariant() );
113 fetchingMore = true;
114 model->fetchMore ( QModelIndex() );
115 fetchingMore = false;
116 Qt::ItemFlags flags = model->flags ( QModelIndex() );
117 QVERIFY( flags == Qt::ItemIsDropEnabled || flags == 0 );
118 model->hasChildren ( QModelIndex() );
119 model->hasIndex ( 0, 0 );
120 model->headerData ( 0, Qt::Horizontal );
121 model->index ( 0, 0 );
122 model->itemData ( QModelIndex() );
123 QVariant cache;
124 model->match ( QModelIndex(), -1, cache );
125 model->mimeTypes();
126 QVERIFY( model->parent ( QModelIndex() ) == QModelIndex() );
127 QVERIFY( model->rowCount() >= 0 );
128 QVariant variant;
129 model->setData ( QModelIndex(), variant, -1 );
130 model->setHeaderData ( -1, Qt::Horizontal, QVariant() );
131 model->setHeaderData ( 999999, Qt::Horizontal, QVariant() );
132 QMap<int, QVariant> roles;
133 model->sibling ( 0, 0, QModelIndex() );
134 model->span ( QModelIndex() );
135 model->supportedDropActions();
138/*!
139 Tests model's implementation of QAbstractItemModel::rowCount() and hasChildren()
141 Models that are dynamically populated are not as fully tested here.
142 */
143void ModelTest::rowCount()
145// qDebug() << "rc";
146 // check top row
147 QModelIndex topIndex = model->index ( 0, 0, QModelIndex() );
148 int rows = model->rowCount ( topIndex );
149 QVERIFY( rows >= 0 );
150 if ( rows > 0 )
151 QVERIFY( model->hasChildren ( topIndex ) );
153 QModelIndex secondLevelIndex = model->index ( 0, 0, topIndex );
154 if ( secondLevelIndex.isValid() ) { // not the top level
155 // check a row count where parent is valid
156 rows = model->rowCount ( secondLevelIndex );
157 QVERIFY( rows >= 0 );
158 if ( rows > 0 )
159 QVERIFY( model->hasChildren ( secondLevelIndex ) );
160 }
162 // The models rowCount() is tested more extensively in checkChildren(),
163 // but this catches the big mistakes
166/*!
167 Tests model's implementation of QAbstractItemModel::columnCount() and hasChildren()
168 */
169void ModelTest::columnCount()
171 // check top row
172 QModelIndex topIndex = model->index ( 0, 0, QModelIndex() );
173 QVERIFY( model->columnCount ( topIndex ) >= 0 );
175 // check a column count where parent is valid
176 QModelIndex childIndex = model->index ( 0, 0, topIndex );
177 if ( childIndex.isValid() )
178 QVERIFY( model->columnCount ( childIndex ) >= 0 );
180 // columnCount() is tested more extensively in checkChildren(),
181 // but this catches the big mistakes
184/*!
185 Tests model's implementation of QAbstractItemModel::hasIndex()
186 */
187void ModelTest::hasIndex()
189// qDebug() << "hi";
190 // Make sure that invalid values returns an invalid index
191 QVERIFY( !model->hasIndex ( -2, -2 ) );
192 QVERIFY( !model->hasIndex ( -2, 0 ) );
193 QVERIFY( !model->hasIndex ( 0, -2 ) );
195 int rows = model->rowCount();
196 int columns = model->columnCount();
198 // check out of bounds
199 QVERIFY( !model->hasIndex ( rows, columns ) );
200 QVERIFY( !model->hasIndex ( rows + 1, columns + 1 ) );
202 if ( rows > 0 )
203 QVERIFY( model->hasIndex ( 0, 0 ) );
205 // hasIndex() is tested more extensively in checkChildren(),
206 // but this catches the big mistakes
209/*!
210 Tests model's implementation of QAbstractItemModel::index()
211 */
212void ModelTest::index()
214// qDebug() << "i";
215 // Make sure that invalid values returns an invalid index
216 QVERIFY( model->index ( -2, -2 ) == QModelIndex() );
217 QVERIFY( model->index ( -2, 0 ) == QModelIndex() );
218 QVERIFY( model->index ( 0, -2 ) == QModelIndex() );
220 int rows = model->rowCount();
221 int columns = model->columnCount();
223 if ( rows == 0 )
224 return;
226 // Catch off by one errors
227 QVERIFY( model->index ( rows, columns ) == QModelIndex() );
228 QVERIFY( model->index ( 0, 0 ).isValid() );
230 // Make sure that the same index is *always* returned
231 QModelIndex a = model->index ( 0, 0 );
232 QModelIndex b = model->index ( 0, 0 );
233 QVERIFY( a == b );
235 // index() is tested more extensively in checkChildren(),
236 // but this catches the big mistakes
239/*!
240 Tests model's implementation of QAbstractItemModel::parent()
241 */
242void ModelTest::parent()
244// qDebug() << "p";
245 // Make sure the model won't crash and will return an invalid QModelIndex
246 // when asked for the parent of an invalid index.
247 QVERIFY( model->parent ( QModelIndex() ) == QModelIndex() );
249 if ( model->rowCount() == 0 )
250 return;
252 // Column 0 | Column 1 |
253 // QModelIndex() | |
254 // \- topIndex | topIndex1 |
255 // \- childIndex | childIndex1 |
257 // Common error test #1, make sure that a top level index has a parent
258 // that is a invalid QModelIndex.
259 QModelIndex topIndex = model->index ( 0, 0, QModelIndex() );
260 QVERIFY( model->parent ( topIndex ) == QModelIndex() );
262 // Common error test #2, make sure that a second level index has a parent
263 // that is the first level index.
264 if ( model->rowCount ( topIndex ) > 0 ) {
265 QModelIndex childIndex = model->index ( 0, 0, topIndex );
266 QVERIFY( model->parent ( childIndex ) == topIndex );
267 }
269 // Common error test #3, the second column should NOT have the same children
270 // as the first column in a row.
271 // Usually the second column shouldn't have children.
272 QModelIndex topIndex1 = model->index ( 0, 1, QModelIndex() );
273 if ( model->rowCount ( topIndex1 ) > 0 ) {
274 QModelIndex childIndex = model->index ( 0, 0, topIndex );
275 QModelIndex childIndex1 = model->index ( 0, 0, topIndex1 );
276 QVERIFY( childIndex != childIndex1 );
277 }
279 // Full test, walk n levels deep through the model making sure that all
280 // parent's children correctly specify their parent.
281 checkChildren ( QModelIndex() );
284/*!
285 Called from the parent() test.
287 A model that returns an index of parent X should also return X when asking
288 for the parent of the index.
290 This recursive function does pretty extensive testing on the whole model in an
291 effort to catch edge cases.
293 This function assumes that rowCount(), columnCount() and index() already work.
294 If they have a bug it will point it out, but the above tests should have already
295 found the basic bugs because it is easier to figure out the problem in
296 those tests then this one.
297 */
298void ModelTest::checkChildren ( const QModelIndex &parent, int currentDepth )
300 // First just try walking back up the tree.
301 QModelIndex p = parent;
302 while ( p.isValid() )
303 p = p.parent();
305 // For models that are dynamically populated
306 if ( model->canFetchMore ( parent ) ) {
307 fetchingMore = true;
308 model->fetchMore ( parent );
309 fetchingMore = false;
310 }
312 int rows = model->rowCount ( parent );
313 int columns = model->columnCount ( parent );
315 if ( rows > 0 )
316 QVERIFY( model->hasChildren ( parent ) );
318 // Some further testing against rows(), columns(), and hasChildren()
319 QVERIFY( rows >= 0 );
320 QVERIFY( columns >= 0 );
321 if ( rows > 0 )
322 QVERIFY( model->hasChildren ( parent ) );
324 //qDebug() << "parent:" << model->data(parent).toString() << "rows:" << rows
325 // << "columns:" << columns << "parent column:" << parent.column();
327 const QModelIndex topLeftChild = model->index( 0, 0, parent );
329 QVERIFY( !model->hasIndex ( rows + 1, 0, parent ) );
330 for ( int r = 0; r < rows; ++r ) {
331 if ( model->canFetchMore ( parent ) ) {
332 fetchingMore = true;
333 model->fetchMore ( parent );
334 fetchingMore = false;
335 }
336 QVERIFY( !model->hasIndex ( r, columns + 1, parent ) );
337 for ( int c = 0; c < columns; ++c ) {
338 QVERIFY( model->hasIndex ( r, c, parent ) );
339 QModelIndex index = model->index ( r, c, parent );
340 // rowCount() and columnCount() said that it existed...
341 QVERIFY( index.isValid() );
343 // index() should always return the same index when called twice in a row
344 QModelIndex modifiedIndex = model->index ( r, c, parent );
345 QVERIFY( index == modifiedIndex );
347 // Make sure we get the same index if we request it twice in a row
348 QModelIndex a = model->index ( r, c, parent );
349 QModelIndex b = model->index ( r, c, parent );
350 QVERIFY( a == b );
352 {
353 const QModelIndex sibling = model->sibling( r, c, topLeftChild );
354 QVERIFY( index == sibling );
355 }
356 {
357 const QModelIndex sibling = topLeftChild.sibling( r, c );
358 QVERIFY( index == sibling );
359 }
361 // Some basic checking on the index that is returned
362 QVERIFY( index.model() == model );
363 QCOMPARE( index.row(), r );
364 QCOMPARE( index.column(), c );
365 // While you can technically return a QVariant usually this is a sign
366 // of a bug in data(). Disable if this really is ok in your model.
367// QVERIFY( model->data ( index, Qt::DisplayRole ).isValid() );
369 // If the next test fails here is some somewhat useful debug you play with.
371 if (model->parent(index) != parent) {
372 qDebug() << r << c << currentDepth << model->data(index).toString()
373 << model->data(parent).toString();
374 qDebug() << index << parent << model->parent(index);
375// And a view that you can even use to show the model.
376// QTreeView view;
377// view.setModel(model);
378// view.show();
379 }
381 // Check that we can get back our real parent.
382 QCOMPARE( model->parent ( index ), parent );
384 // recursively go down the children
385 if ( model->hasChildren ( index ) && currentDepth < 10 ) {
386 //qDebug() << r << c << "has children" << model->rowCount(index);
387 checkChildren ( index, ++currentDepth );
388 }/* else { if (currentDepth >= 10) qDebug() << "checked 10 deep"; };*/
390 // make sure that after testing the children that the index doesn't change.
391 QModelIndex newerIndex = model->index ( r, c, parent );
392 QVERIFY( index == newerIndex );
393 }
394 }
397/*!
398 Tests model's implementation of QAbstractItemModel::data()
399 */
400void ModelTest::data()
402 // Invalid index should return an invalid qvariant
403 QVERIFY( !model->data ( QModelIndex() ).isValid() );
405 if ( model->rowCount() == 0 )
406 return;
408 // A valid index should have a valid QVariant data
409 QVERIFY( model->index ( 0, 0 ).isValid() );
411 // shouldn't be able to set data on an invalid index
412 QVERIFY( !model->setData ( QModelIndex(), QLatin1String ( "foo" ), Qt::DisplayRole ) );
414 // General Purpose roles that should return a QString
415 QVariant variant = model->data ( model->index ( 0, 0 ), Qt::ToolTipRole );
416 if ( variant.isValid() ) {
417 QVERIFY( variant.canConvert<QString>() );
418 }
419 variant = model->data ( model->index ( 0, 0 ), Qt::StatusTipRole );
420 if ( variant.isValid() ) {
421 QVERIFY( variant.canConvert<QString>() );
422 }
423 variant = model->data ( model->index ( 0, 0 ), Qt::WhatsThisRole );
424 if ( variant.isValid() ) {
425 QVERIFY( variant.canConvert<QString>() );
426 }
428 // General Purpose roles that should return a QSize
429 variant = model->data ( model->index ( 0, 0 ), Qt::SizeHintRole );
430 if ( variant.isValid() ) {
431 QVERIFY( variant.canConvert<QSize>() );
432 }
434 // General Purpose roles that should return a QFont
435 QVariant fontVariant = model->data ( model->index ( 0, 0 ), Qt::FontRole );
436 if ( fontVariant.isValid() ) {
437 QVERIFY( fontVariant.canConvert<QFont>() );
438 }
440 // Check that the alignment is one we know about
441 QVariant textAlignmentVariant = model->data ( model->index ( 0, 0 ), Qt::TextAlignmentRole );
442 if ( textAlignmentVariant.isValid() ) {
443 int alignment = textAlignmentVariant.toInt();
444 QCOMPARE( alignment, ( alignment & ( Qt::AlignHorizontal_Mask | Qt::AlignVertical_Mask ) ) );
445 }
447 // General Purpose roles that should return a QColor
448 QVariant colorVariant = model->data ( model->index ( 0, 0 ), Qt::BackgroundColorRole );
449 if ( colorVariant.isValid() ) {
450 QVERIFY( colorVariant.canConvert<QColor>() );
451 }
453 colorVariant = model->data ( model->index ( 0, 0 ), Qt::TextColorRole );
454 if ( colorVariant.isValid() ) {
455 QVERIFY( colorVariant.canConvert<QColor>() );
456 }
458 // Check that the "check state" is one we know about.
459 QVariant checkStateVariant = model->data ( model->index ( 0, 0 ), Qt::CheckStateRole );
460 if ( checkStateVariant.isValid() ) {
461 int state = checkStateVariant.toInt();
462 QVERIFY( state == Qt::Unchecked ||
463 state == Qt::PartiallyChecked ||
464 state == Qt::Checked );
465 }
468/*!
469 Store what is about to be inserted to make sure it actually happens
471 \sa rowsInserted()
472 */
473void ModelTest::rowsAboutToBeInserted ( const QModelIndex &parent, int start, int /* end */)
475// Q_UNUSED(end);
476// qDebug() << "rowsAboutToBeInserted" << "start=" << start << "end=" << end << "parent=" << model->data ( parent ).toString()
477// << "current count of parent=" << model->rowCount ( parent ); // << "display of last=" << model->data( model->index(start-1, 0, parent) );
478// qDebug() << model->index(start-1, 0, parent) << model->data( model->index(start-1, 0, parent) );
479 Changing c;
480 c.parent = parent;
481 c.oldSize = model->rowCount ( parent );
482 c.last = model->data ( model->index ( start - 1, 0, parent ) );
483 c.next = model->data ( model->index ( start, 0, parent ) );
484 insert.push ( c );
487/*!
488 Confirm that what was said was going to happen actually did
490 \sa rowsAboutToBeInserted()
491 */
492void ModelTest::rowsInserted ( const QModelIndex & parent, int start, int end )
494 Changing c = insert.pop();
495 QVERIFY( c.parent == parent );
496// qDebug() << "rowsInserted" << "start=" << start << "end=" << end << "oldsize=" << c.oldSize
497// << "parent=" << model->data ( parent ).toString() << "current rowcount of parent=" << model->rowCount ( parent );
499// for (int ii=start; ii <= end; ii++)
500// {
501// qDebug() << "itemWasInserted:" << ii << model->data ( model->index ( ii, 0, parent ));
502// }
503// qDebug();
505 QVERIFY( c.oldSize + ( end - start + 1 ) == model->rowCount ( parent ) );
506 QVERIFY( c.last == model->data ( model->index ( start - 1, 0, c.parent ) ) );
508 if (c.next != model->data(model->index(end + 1, 0, c.parent))) {
509 qDebug() << start << end;
510 for (int i=0; i < model->rowCount(); ++i)
511 qDebug() << model->index(i, 0).data().toString();
512 qDebug() << c.next << model->data(model->index(end + 1, 0, c.parent));
513 }
515 QVERIFY( c.next == model->data ( model->index ( end + 1, 0, c.parent ) ) );
518void ModelTest::layoutAboutToBeChanged()
520 for ( int i = 0; i < qBound ( 0, model->rowCount(), 100 ); ++i )
521 changing.append ( QPersistentModelIndex ( model->index ( i, 0 ) ) );
524void ModelTest::layoutChanged()
526 for ( int i = 0; i < changing.count(); ++i ) {
527 QPersistentModelIndex p = changing[i];
528 QVERIFY( p == model->index ( p.row(), p.column(), p.parent() ) );
529 }
530 changing.clear();
533/*!
534 Store what is about to be inserted to make sure it actually happens
536 \sa rowsRemoved()
537 */
538void ModelTest::rowsAboutToBeRemoved ( const QModelIndex &parent, int start, int end )
540qDebug() << "ratbr" << parent << start << end;
541 Changing c;
542 c.parent = parent;
543 c.oldSize = model->rowCount ( parent );
544 c.last = model->data ( model->index ( start - 1, 0, parent ) );
545 c.next = model->data ( model->index ( end + 1, 0, parent ) );
546 remove.push ( c );
549/*!
550 Confirm that what was said was going to happen actually did
552 \sa rowsAboutToBeRemoved()
553 */
554void ModelTest::rowsRemoved ( const QModelIndex & parent, int start, int end )
556 qDebug() << "rr" << parent << start << end;
557 Changing c = remove.pop();
558 QVERIFY( c.parent == parent );
559 QVERIFY( c.oldSize - ( end - start + 1 ) == model->rowCount ( parent ) );
560 QVERIFY( c.last == model->data ( model->index ( start - 1, 0, c.parent ) ) );
561 QVERIFY( c.next == model->data ( model->index ( start, 0, c.parent ) ) );
564void ModelTest::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
566 QVERIFY(topLeft.isValid());
567 QVERIFY(bottomRight.isValid());
568 QModelIndex commonParent = bottomRight.parent();
569 QVERIFY(topLeft.parent() == commonParent);
570 QVERIFY(topLeft.row() <= bottomRight.row());
571 QVERIFY(topLeft.column() <= bottomRight.column());
572 int rowCount = model->rowCount(commonParent);
573 int columnCount = model->columnCount(commonParent);
574 QVERIFY(bottomRight.row() < rowCount);
575 QVERIFY(bottomRight.column() < columnCount);
578void ModelTest::headerDataChanged(Qt::Orientation orientation, int start, int end)
580 QVERIFY(start >= 0);
581 QVERIFY(end >= 0);
582 QVERIFY(start <= end);
583 int itemCount = orientation == Qt::Vertical ? model->rowCount() : model->columnCount();
584 QVERIFY(start < itemCount);
585 QVERIFY(end < itemCount);