1
#############################################################################
3
## Copyright (C) 2007 Trolltech ASA. All rights reserved.
5
## This file is part of the Qt Concurrent project on Trolltech Labs.
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
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.
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.
22
#############################################################################
25
from PyQt4 import QtCore, QtGui
28
class ModelTest(QtCore.QObject):
29
def __init__(self, _model, parent):
31
Connect to all of the models signals, Whenever anything happens recheck everything.
33
QtCore.QObject.__init__(self,parent)
35
self.model = sip.cast(_model, QtCore.QAbstractItemModel)
38
self.fetchingMore = False
41
self.connect( self.model, QtCore.SIGNAL("columnsAboutToBeInserted(const QtCore.QModelIndex&, int, int)"), self.runAllTests)
42
self.connect( self.model, QtCore.SIGNAL("columnsAboutToBeRemoved(const QtCore.QModelIndex&, int, int)"), self.runAllTests)
43
self.connect( self.model, QtCore.SIGNAL("columnsBeInserted(const QtCore.QModelIndex&, int, int)"), self.runAllTests)
44
self.connect( self.model, QtCore.SIGNAL("columnsRemoved(const QtCore.QModelIndex&, int, int)"), self.runAllTests)
45
self.connect( self.model, QtCore.SIGNAL("dataChanged(const QtCore.QModelIndex&, const QtCore.QModelIndex&)"), self.runAllTests)
46
self.connect( self.model, QtCore.SIGNAL("headerDataChanged(Qt::Orientation, int, int)"), self.runAllTests)
47
self.connect( self.model, QtCore.SIGNAL("layoutAboutToBeChanged()"), self.runAllTests)
48
self.connect( self.model, QtCore.SIGNAL("layoutChanged()"), self.runAllTests)
49
self.connect( self.model, QtCore.SIGNAL("modelReset()"), self.runAllTests)
50
self.connect( self.model, QtCore.SIGNAL("rowsAboutToBeInserted(const QtCore.QModelIndex&, int, int)"), self.runAllTests)
51
self.connect( self.model, QtCore.SIGNAL("rowsAboutToBeRemoved(const QtCore.QModelIndex&, int, int)"), self.runAllTests)
52
self.connect( self.model, QtCore.SIGNAL("rowsBeInserted(const QtCore.QModelIndex&, int, int)"), self.runAllTests)
53
self.connect( self.model, QtCore.SIGNAL("rowsRemoved(const QtCore.QModelIndex&, int, int)"), self.runAllTests)
55
# Special checks for inserting/removing
56
self.connect( self.model, QtCore.SIGNAL("rowsAboutToBeInserted(const QtCore.QModelIndex&, int, int)"), self.rowsAboutToBeInserted)
57
self.connect( self.model, QtCore.SIGNAL("rowsAboutToBeRemoved(const QtCore.QModelIndex&, int, int)"), self.rowsAboutToBeRemoved)
58
self.connect( self.model, QtCore.SIGNAL("rowsBeInserted(const QtCore.QModelIndex&, int, int)"), self.rowsInserted)
59
self.connect( self.model, QtCore.SIGNAL("rowsRemoved(const QtCore.QModelIndex&, int, int)"), self.rowsRemoved)
62
def nonDestructiveBasicTest(self):
64
nonDestructiveBasicTest tries to call a number of the basic functions (not all)
65
to make sure the model doesn't outright segfault, testing the functions that makes sense.
67
assert(self.model.buddy(QtCore.QModelIndex()) == QtCore.QModelIndex())
68
self.model.canFetchMore(QtCore.QModelIndex())
69
assert(self.model.columnCount(QtCore.QModelIndex()) >= 0)
70
assert(self.model.data(QtCore.QModelIndex(), QtCore.Qt.DisplayRole) == QtCore.QVariant())
71
self.fetchingMore = True
72
self.model.fetchMore(QtCore.QModelIndex())
73
self.fetchingMore = False
74
flags = self.model.flags(QtCore.QModelIndex())
75
assert( int(flags & QtCore.Qt.ItemIsEnabled) == QtCore.Qt.ItemIsEnabled or int(flags & QtCore.Qt.ItemIsEnabled ) == 0 )
76
self.model.hasChildren(QtCore.QModelIndex())
77
self.model.hasIndex(0,0)
78
self.model.headerData(0,QtCore.Qt.Horizontal, QtCore.Qt.DisplayRole)
79
self.model.index(0,0, QtCore.QModelIndex())
80
self.model.itemData(QtCore.QModelIndex())
81
cache = QtCore.QVariant()
82
self.model.match(QtCore.QModelIndex(), -1, cache)
83
self.model.mimeTypes()
84
assert(self.model.parent(QtCore.QModelIndex()) == QtCore.QModelIndex())
85
assert(self.model.rowCount(QtCore.QModelIndex()) >= 0)
86
variant = QtCore.QVariant()
87
self.model.setData(QtCore.QModelIndex(), variant, -1)
88
self.model.setHeaderData(-1, QtCore.Qt.Horizontal, QtCore.QVariant())
89
self.model.setHeaderData(0, QtCore.Qt.Horizontal, QtCore.QVariant())
90
self.model.setHeaderData(999999, QtCore.Qt.Horizontal, QtCore.QVariant())
91
self.model.sibling(0,0,QtCore.QModelIndex())
92
self.model.span(QtCore.QModelIndex())
93
self.model.supportedDropActions()
97
Tests self.model's implementation of QtCore.QAbstractItemModel::rowCount() and hasChildren()
99
self.models that are dynamically populated are not as fully tested here.
102
topindex = self.model.index(0,0,QtCore.QModelIndex())
103
rows = self.model.rowCount(topindex)
106
assert(self.model.hasChildren(topindex) == True )
108
secondlvl = self.model.index(0,0,topindex)
109
if secondlvl.isValid():
110
# check a row count where parent is valid
111
rows = self.model.rowCount(secondlvl)
114
assert(self.model.hasChildren(secondlvl) == True)
116
# The self.models rowCount() is tested more extensively in checkChildren,
117
# but this catches the big mistakes
119
def columnCount(self):
121
Tests self.model's implementation of QtCore.QAbstractItemModel::columnCount() and hasChildren()
124
topidx = self.model.index(0,0,QtCore.QModelIndex())
125
assert(self.model.columnCount(topidx) >= 0)
127
# check a column count where parent is valid
128
childidx = self.model.index(0,0,topidx)
129
if childidx.isValid() :
130
assert(self.model.columnCount(childidx) >= 0)
132
# columnCount() is tested more extensively in checkChildren,
133
# but this catches the big mistakes
137
Tests self.model's implementation of QtCore.QAbstractItemModel::hasIndex()
139
# Make sure that invalid values returns an invalid index
140
assert(self.model.hasIndex(-2,-2) == False)
141
assert(self.model.hasIndex(-2,0) == False)
142
assert(self.model.hasIndex(0,-2) == False)
144
rows = self.model.rowCount(QtCore.QModelIndex())
145
cols = self.model.columnCount(QtCore.QModelIndex())
147
# check out of bounds
148
assert(self.model.hasIndex(rows,cols) == False)
149
assert(self.model.hasIndex(rows+1,cols+1) == False)
152
assert(self.model.hasIndex(0,0) == True)
154
# hasIndex() is tested more extensively in checkChildren()
155
# but this catches the big mistakes
159
Tests self.model's implementation of QtCore.QAbstractItemModel::index()
161
# Make sure that invalid values returns an invalid index
162
assert(self.model.index(-2,-2, QtCore.QModelIndex()) == QtCore.QModelIndex())
163
assert(self.model.index(-2,0, QtCore.QModelIndex()) == QtCore.QModelIndex())
164
assert(self.model.index(0,-2, QtCore.QModelIndex()) == QtCore.QModelIndex())
166
rows = self.model.rowCount(QtCore.QModelIndex())
167
cols = self.model.columnCount(QtCore.QModelIndex())
172
# Catch off by one errors
173
assert(self.model.index(rows,cols, QtCore.QModelIndex()) == QtCore.QModelIndex())
174
assert(self.model.index(0,0, QtCore.QModelIndex()).isValid() == True)
176
# Make sure that the same index is *always* returned
177
a = self.model.index(0,0, QtCore.QModelIndex())
178
b = self.model.index(0,0, QtCore.QModelIndex())
181
# index() is tested more extensively in checkChildren()
182
# but this catches the big mistakes
186
Tests self.model's implementation of QtCore.QAbstractItemModel::parent()
188
# Make sure the self.model wont crash and will return an invalid QtCore.QModelIndex
189
# when asked for the parent of an invalid index
190
assert(self.model.parent(QtCore.QModelIndex()) == QtCore.QModelIndex())
192
if self.model.rowCount(QtCore.QModelIndex()) == 0:
195
# Column 0 | Column 1 |
196
# QtCore.Qself.modelIndex() | |
197
# \- topidx | topidx1 |
198
# \- childix | childidx1 |
200
# Common error test #1, make sure that a top level index has a parent
201
# that is an invalid QtCore.Qself.modelIndex
202
topidx = self.model.index(0,0,QtCore.QModelIndex())
203
assert(self.model.parent(topidx) == QtCore.QModelIndex())
205
# Common error test #2, make sure that a second level index has a parent
206
# that is the first level index
207
if self.model.rowCount(topidx) > 0 :
208
childidx = self.model.index(0,0,topidx)
209
assert(self.model.parent(childidx) == topidx)
211
# Common error test #3, the second column should NOT have the same children
212
# as the first column in a row
213
# Usually the second column shouldn't have children
214
topidx1 = self.model.index(0,1,QtCore.QModelIndex())
215
if self.model.rowCount(topidx1) > 0:
216
childidx = self.model.index(0,0,topidx)
217
childidx1 = self.model.index(0,0,topidx1)
218
assert(childidx != childidx1)
220
# Full test, walk n levels deep through the self.model making sure that all
221
# parent's children correctly specify their parent
222
self.checkChildren(QtCore.QModelIndex())
226
Tests self.model's implementation of QtCore.QAbstractItemModel::data()
228
# Invalid index should return an invalid qvariant
229
assert( not self.model.data(QtCore.QModelIndex(), QtCore.Qt.DisplayRole).isValid())
231
if self.model.rowCount(QtCore.QModelIndex()) == 0:
234
# A valid index should have a valid QtCore.QVariant data
235
assert( self.model.index(0,0, QtCore.QModelIndex()).isValid())
237
# shouldn't be able to set data on an invalid index
238
assert( self.model.setData( QtCore.QModelIndex(), QtCore.QVariant("foo"), QtCore.Qt.DisplayRole) == False)
240
# General Purpose roles that should return a QString
241
variant = self.model.data(self.model.index(0,0,QtCore.QModelIndex()), QtCore.Qt.ToolTipRole)
242
if variant.isValid():
243
assert( variant.canConvert( QtCore.QVariant.String ) )
244
variant = self.model.data(self.model.index(0,0,QtCore.QModelIndex()), QtCore.Qt.StatusTipRole)
245
if variant.isValid():
246
assert( variant.canConvert( QtCore.QVariant.String ) )
247
variant = self.model.data(self.model.index(0,0,QtCore.QModelIndex()), QtCore.Qt.WhatsThisRole)
248
if variant.isValid():
249
assert( variant.canConvert( QtCore.QVariant.String ) )
251
# General Purpose roles that should return a QSize
252
variant = self.model.data(self.model.index(0,0,QtCore.QModelIndex()), QtCore.Qt.SizeHintRole)
253
if variant.isValid():
254
assert( variant.canConvert( QtCore.QVariant.Size ) )
256
# General Purpose roles that should return a QFont
257
variant = self.model.data(self.model.index(0,0,QtCore.QModelIndex()), QtCore.Qt.FontRole)
258
if variant.isValid():
259
assert( variant.canConvert( QtCore.QVariant.Font ) )
261
# Check that the alignment is one we know about
262
variant = self.model.data(self.model.index(0,0,QtCore.QModelIndex()), QtCore.Qt.TextAlignmentRole)
263
if variant.isValid():
264
alignment = variant.toInt()[0]
265
assert( alignment == QtCore.Qt.AlignLeft or
266
alignment == QtCore.Qt.AlignRight or
267
alignment == QtCore.Qt.AlignHCenter or
268
alignment == QtCore.Qt.AlignJustify)
270
# General Purpose roles that should return a QColor
271
variant = self.model.data(self.model.index(0,0,QtCore.QModelIndex()), QtCore.Qt.BackgroundColorRole)
272
if variant.isValid():
273
assert( variant.canConvert( QtCore.QVariant.Color ) )
274
variant = self.model.data(self.model.index(0,0,QtCore.QModelIndex()), QtCore.Qt.TextColorRole)
275
if variant.isValid():
276
assert( variant.canConvert( QtCore.QVariant.Color ) )
278
# Check that the "check state" is one we know about.
279
variant = self.model.data(self.model.index(0,0,QtCore.QModelIndex()), QtCore.Qt.CheckStateRole)
280
if variant.isValid():
281
state = variant.toInt()[0]
282
assert( state == QtCore.Qt.Unchecked or
283
state == QtCore.Qt.PartiallyChecked or
284
state == QtCore.Qt.Checked )
287
def runAllTests(self):
288
if self.fetchingMore:
290
self.nonDestructiveBasicTest()
298
def rowsAboutToBeInserted(self, parent, start, end):
300
Store what is about to be inserted to make sure it actually happens
304
c['oldSize'] = self.model.rowCount(parent)
305
c['last'] = self.model.data(model.index(start-1, 0, parent))
306
c['next'] = self.model.data(model.index(start, 0, parent))
309
def rowsInserted(self, parent, start, end):
311
Confirm that what was said was going to happen actually did
314
assert(c['parent'] == parent)
315
assert(c['oldSize'] + (end - start + 1) == self.model.rowCount(parent))
316
assert(c['last'] == self.model.data(model.index(start-1, 0, c['parent'])))
318
# if c['next'] != self.model.data(model.index(end+1, 0, c['parent'])):
319
# qDebug << start << end
320
# for i in range(0, self.model.rowCount(QtCore.QModelIndex())):
321
# qDebug << self.model.index(i, 0).data().toString()
322
# qDebug() << c['next'] << self.model.data(model.index(end+1, 0, c['parent']))
324
assert(c['next'] == self.model.data(model.index(end+1, 0, c['parent'])))
326
def rowsAboutToBeRemoved(self, parent, start, end):
328
Store what is about to be inserted to make sure it actually happens
332
c['oldSize'] = self.model.rowCount(parent)
333
c['last'] = self.model.data(model.index(start-1, 0, parent))
334
c['next'] = self.model.data(model.index(end+1, 0, parent))
337
def rowsRemoved(self, parent, start, end):
339
Confirm that what was said was going to happen actually did
342
assert(c['parent'] == parent)
343
assert(c['oldSize'] - (end - start + 1) == self.model.rowCount(parent))
344
assert(c['last'] == self.model.data(model.index(start-1, 0, c['parent'])))
345
assert(c['next'] == self.model.data(model.index(start, 0, c['parent'])))
347
def checkChildren(self, parent, depth = 0):
349
Called from parent() test.
351
A self.model that returns an index of parent X should also return X when asking
352
for the parent of the index
354
This recursive function does pretty extensive testing on the whole self.model in an
355
effort to catch edge cases.
357
This function assumes that rowCount(QtCore.QModelIndex()), columnCount(QtCore.QModelIndex()) and index() already work.
358
If they have a bug it will point it out, but the above tests should have already
359
found the basic bugs because it is easier to figure out the problem in
360
those tests then this one
362
# First just try walking back up the tree.
367
#For self.models that are dynamically populated
368
if self.model.canFetchMore( parent ):
369
self.fetchingMore = True
370
self.model.fetchMore(parent)
371
self.fetchingMore = False
373
rows = self.model.rowCount(parent)
374
cols = self.model.columnCount(parent)
377
assert(self.model.hasChildren(parent))
379
# Some further testing against rows(), columns, and hasChildren()
384
assert(self.model.hasChildren(parent) == True)
386
# qDebug() << "parent:" << self.model.data(parent).toString() << "rows:" << rows
387
# << "columns:" << cols << "parent column:" << parent.column()
389
assert( self.model.hasIndex( rows+1, 0, parent) == False)
390
for r in range(0,rows):
391
if self.model.canFetchMore(parent):
392
self.fetchingMore = True
393
self.model.fetchMore(parent)
394
self.fetchingMore = False
395
assert(self.model.hasIndex(r,cols+1,parent) == False)
396
for c in range(0,cols):
397
assert(self.model.hasIndex(r,c,parent))
398
index = self.model.index(r,c,parent)
399
# rowCount(QtCore.QModelIndex()) and columnCount(QtCore.QModelIndex()) said that it existed...
400
assert(index.isValid() == True)
402
# index() should always return the same index when called twice in a row
403
modIdx = self.model.index(r,c,parent)
404
assert(index == modIdx)
406
# Make sure we get the same index if we request it twice in a row
407
a = self.model.index(r,c,parent)
408
b = self.model.index(r,c,parent)
411
# Some basic checking on the index that is returned
412
# assert( index.model() == self.model )
413
# This raises an error that is not part of the qbzr code.
414
# see http://www.opensubscriber.com/message/pyqt@riverbankcomputing.com/10335500.html
415
assert( index.row() == r )
416
assert( index.column() == c )
417
# While you can technically return a QtCore.QVariant usually this is a sign
418
# if an bug in data() Disable if this really is ok in your self.model
419
assert( self.model.data(index, QtCore.Qt.DisplayRole).isValid() == True )
421
#if the next test fails here is some somehwat useful debug you play with
422
# if self.model.parent(index) != parent:
423
# qDebug() << r << c << depth << self.model.data(index).toString()
424
# << self.model.data(parent).toString()
425
# qDebug() << index << parent << self.model.parent(index)
426
# # And a view that you can even use to show the self.model
427
# # view = QtGui.QTreeView()
428
# # view.setself.model(model)
432
# Check that we can get back our real parent
433
p = self.model.parent( index )
434
assert( p.internalId() == parent.internalId() )
435
assert( p.row() == parent.row() )
437
# recursively go down the children
438
if self.model.hasChildren(index) and depth < 10:
439
# qDebug() << r << c << "hasChildren" << self.model.rowCount(index)
440
self.checkChildren(index, ++depth)
443
# qDebug() << "checked 10 deep"
445
# Make sure that after testing the children that the index doesn't change
446
newIdx = self.model.index(r,c,parent)
447
assert(index == newIdx)