diff options
author | Sandro Knauß <sknauss@kde.org> | 2016-10-18 15:28:45 +0200 |
---|---|---|
committer | Sandro Knauß <sknauss@kde.org> | 2016-10-18 15:28:45 +0200 |
commit | 0fa8aa51aac15233820b9f9c576584e6ff8ee151 (patch) | |
tree | aeea1a5050fc353caaf95ed32c5abdcacf73aefb | |
parent | d15a02d3c26c24530e8d9360629212e419c81c79 (diff) | |
download | kube-0fa8aa51aac15233820b9f9c576584e6ff8ee151.tar.gz kube-0fa8aa51aac15233820b9f9c576584e6ff8ee151.zip |
use modeltest fromupstream to make sure, we have a valid model
-rw-r--r-- | framework/domain/CMakeLists.txt | 5 | ||||
-rw-r--r-- | framework/domain/messageparser.cpp | 5 | ||||
-rw-r--r-- | framework/domain/modeltest.cpp | 588 | ||||
-rw-r--r-- | framework/domain/modeltest.h | 83 |
4 files changed, 678 insertions, 3 deletions
diff --git a/framework/domain/CMakeLists.txt b/framework/domain/CMakeLists.txt index 55bc2f24..a31bb016 100644 --- a/framework/domain/CMakeLists.txt +++ b/framework/domain/CMakeLists.txt | |||
@@ -10,6 +10,7 @@ set(mailplugin_SRCS | |||
10 | messageparser_new.cpp | 10 | messageparser_new.cpp |
11 | messageparser_old.cpp | 11 | messageparser_old.cpp |
12 | mailtemplates.cpp | 12 | mailtemplates.cpp |
13 | modeltest.cpp | ||
13 | retriever.cpp | 14 | retriever.cpp |
14 | accountfactory.cpp | 15 | accountfactory.cpp |
15 | accountscontroller.cpp | 16 | accountscontroller.cpp |
@@ -22,7 +23,7 @@ find_package(KF5 REQUIRED COMPONENTS Package) | |||
22 | 23 | ||
23 | add_library(mailplugin SHARED ${mailplugin_SRCS}) | 24 | add_library(mailplugin SHARED ${mailplugin_SRCS}) |
24 | 25 | ||
25 | qt5_use_modules(mailplugin Core Quick Qml WebKitWidgets) | 26 | qt5_use_modules(mailplugin Core Quick Qml WebKitWidgets Test) |
26 | target_link_libraries(mailplugin actionplugin settingsplugin sink mimetreeparser KF5::MimeTreeParser KF5::Codecs KF5::Package KF5::Async KF5::IconThemes) | 27 | target_link_libraries(mailplugin actionplugin settingsplugin sink mimetreeparser KF5::MimeTreeParser KF5::Codecs KF5::Package KF5::Async KF5::IconThemes) |
27 | 28 | ||
28 | add_subdirectory(actions/tests) | 29 | add_subdirectory(actions/tests) |
@@ -30,4 +31,4 @@ add_subdirectory(actions/tests) | |||
30 | install(TARGETS mailplugin DESTINATION ${QML_INSTALL_DIR}/org/kube/framework/domain) | 31 | install(TARGETS mailplugin DESTINATION ${QML_INSTALL_DIR}/org/kube/framework/domain) |
31 | install(FILES qmldir DESTINATION ${QML_INSTALL_DIR}/org/kube/framework/domain) | 32 | install(FILES qmldir DESTINATION ${QML_INSTALL_DIR}/org/kube/framework/domain) |
32 | 33 | ||
33 | add_subdirectory(mimetreeparser) \ No newline at end of file | 34 | add_subdirectory(mimetreeparser) |
diff --git a/framework/domain/messageparser.cpp b/framework/domain/messageparser.cpp index ef9fb0d2..ec79d50b 100644 --- a/framework/domain/messageparser.cpp +++ b/framework/domain/messageparser.cpp | |||
@@ -18,6 +18,7 @@ | |||
18 | */ | 18 | */ |
19 | #include "messageparser.h" | 19 | #include "messageparser.h" |
20 | 20 | ||
21 | #include "modeltest.h" | ||
21 | #include "stringhtmlwriter.h" | 22 | #include "stringhtmlwriter.h" |
22 | #include "objecttreesource.h" | 23 | #include "objecttreesource.h" |
23 | 24 | ||
@@ -100,5 +101,7 @@ QAbstractItemModel *MessageParser::partTree() const | |||
100 | 101 | ||
101 | QAbstractItemModel *MessageParser::newTree() const | 102 | QAbstractItemModel *MessageParser::newTree() const |
102 | { | 103 | { |
103 | return new NewModel(d->mParser); | 104 | const auto model = new NewModel(d->mParser); |
105 | new ModelTest(model, model); | ||
106 | return model; | ||
104 | } | 107 | } |
diff --git a/framework/domain/modeltest.cpp b/framework/domain/modeltest.cpp new file mode 100644 index 00000000..295ff6d0 --- /dev/null +++ b/framework/domain/modeltest.cpp | |||
@@ -0,0 +1,588 @@ | |||
1 | /**************************************************************************** | ||
2 | ** | ||
3 | ** Copyright (C) 2016 The Qt Company Ltd. | ||
4 | ** Contact: https://www.qt.io/licensing/ | ||
5 | ** | ||
6 | ** This file is part of the test suite of the Qt Toolkit. | ||
7 | ** | ||
8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ | ||
9 | ** Commercial License Usage | ||
10 | ** Licensees holding valid commercial Qt licenses may use this file in | ||
11 | ** accordance with the commercial license agreement provided with the | ||
12 | ** Software or, alternatively, in accordance with the terms contained in | ||
13 | ** a written agreement between you and The Qt Company. For licensing terms | ||
14 | ** and conditions see https://www.qt.io/terms-conditions. For further | ||
15 | ** information use the contact form at https://www.qt.io/contact-us. | ||
16 | ** | ||
17 | ** GNU General Public License Usage | ||
18 | ** Alternatively, this file may be used under the terms of the GNU | ||
19 | ** General Public License version 3 as published by the Free Software | ||
20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT | ||
21 | ** included in the packaging of this file. Please review the following | ||
22 | ** information to ensure the GNU General Public License requirements will | ||
23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. | ||
24 | ** | ||
25 | ** $QT_END_LICENSE$ | ||
26 | ** | ||
27 | ****************************************************************************/ | ||
28 | |||
29 | #include "modeltest.h" | ||
30 | |||
31 | #include <QtCore/QtCore> | ||
32 | #include <QtTest/QtTest> | ||
33 | |||
34 | #include <QDebug> | ||
35 | /*! | ||
36 | Connect to all of the models signals. Whenever anything happens recheck everything. | ||
37 | */ | ||
38 | ModelTest::ModelTest ( QAbstractItemModel *_model, QObject *parent ) : QObject ( parent ), model ( _model ), fetchingMore ( false ) | ||
39 | { | ||
40 | if (!model) | ||
41 | qFatal("%s: model must not be null", Q_FUNC_INFO); | ||
42 | |||
43 | connect(model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)), | ||
44 | this, SLOT(runAllTests()) ); | ||
45 | connect(model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)), | ||
46 | this, SLOT(runAllTests()) ); | ||
47 | connect(model, SIGNAL(columnsInserted(QModelIndex,int,int)), | ||
48 | this, SLOT(runAllTests()) ); | ||
49 | connect(model, SIGNAL(columnsRemoved(QModelIndex,int,int)), | ||
50 | this, SLOT(runAllTests()) ); | ||
51 | connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), | ||
52 | this, SLOT(runAllTests()) ); | ||
53 | connect(model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)), | ||
54 | this, SLOT(runAllTests()) ); | ||
55 | connect(model, SIGNAL(layoutAboutToBeChanged()), this, SLOT(runAllTests()) ); | ||
56 | connect(model, SIGNAL(layoutChanged()), this, SLOT(runAllTests()) ); | ||
57 | connect(model, SIGNAL(modelReset()), this, SLOT(runAllTests()) ); | ||
58 | connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), | ||
59 | this, SLOT(runAllTests()) ); | ||
60 | connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), | ||
61 | this, SLOT(runAllTests()) ); | ||
62 | connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), | ||
63 | this, SLOT(runAllTests()) ); | ||
64 | connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), | ||
65 | this, SLOT(runAllTests()) ); | ||
66 | |||
67 | // Special checks for changes | ||
68 | connect(model, SIGNAL(layoutAboutToBeChanged()), | ||
69 | this, SLOT(layoutAboutToBeChanged()) ); | ||
70 | connect(model, SIGNAL(layoutChanged()), | ||
71 | this, SLOT(layoutChanged()) ); | ||
72 | |||
73 | connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), | ||
74 | this, SLOT(rowsAboutToBeInserted(QModelIndex,int,int)) ); | ||
75 | connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), | ||
76 | this, SLOT(rowsAboutToBeRemoved(QModelIndex,int,int)) ); | ||
77 | connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), | ||
78 | this, SLOT(rowsInserted(QModelIndex,int,int)) ); | ||
79 | connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), | ||
80 | this, SLOT(rowsRemoved(QModelIndex,int,int)) ); | ||
81 | connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), | ||
82 | this, SLOT(dataChanged(QModelIndex,QModelIndex)) ); | ||
83 | connect(model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)), | ||
84 | this, SLOT(headerDataChanged(Qt::Orientation,int,int)) ); | ||
85 | |||
86 | runAllTests(); | ||
87 | } | ||
88 | |||
89 | void ModelTest::runAllTests() | ||
90 | { | ||
91 | if ( fetchingMore ) | ||
92 | return; | ||
93 | nonDestructiveBasicTest(); | ||
94 | rowCount(); | ||
95 | columnCount(); | ||
96 | hasIndex(); | ||
97 | index(); | ||
98 | parent(); | ||
99 | data(); | ||
100 | } | ||
101 | |||
102 | /*! | ||
103 | nonDestructiveBasicTest tries to call a number of the basic functions (not all) | ||
104 | to make sure the model doesn't outright segfault, testing the functions that makes sense. | ||
105 | */ | ||
106 | void ModelTest::nonDestructiveBasicTest() | ||
107 | { | ||
108 | QVERIFY( model->buddy ( QModelIndex() ) == QModelIndex() ); | ||
109 | model->canFetchMore ( QModelIndex() ); | ||
110 | QVERIFY( model->columnCount ( QModelIndex() ) >= 0 ); | ||
111 | QVERIFY( model->data ( QModelIndex() ) == QVariant() ); | ||
112 | fetchingMore = true; | ||
113 | model->fetchMore ( QModelIndex() ); | ||
114 | fetchingMore = false; | ||
115 | Qt::ItemFlags flags = model->flags ( QModelIndex() ); | ||
116 | QVERIFY( flags == Qt::ItemIsDropEnabled || flags == 0 ); | ||
117 | model->hasChildren ( QModelIndex() ); | ||
118 | model->hasIndex ( 0, 0 ); | ||
119 | model->headerData ( 0, Qt::Horizontal ); | ||
120 | model->index ( 0, 0 ); | ||
121 | model->itemData ( QModelIndex() ); | ||
122 | QVariant cache; | ||
123 | model->match ( QModelIndex(), -1, cache ); | ||
124 | model->mimeTypes(); | ||
125 | QVERIFY( model->parent ( QModelIndex() ) == QModelIndex() ); | ||
126 | QVERIFY( model->rowCount() >= 0 ); | ||
127 | QVariant variant; | ||
128 | model->setData ( QModelIndex(), variant, -1 ); | ||
129 | model->setHeaderData ( -1, Qt::Horizontal, QVariant() ); | ||
130 | model->setHeaderData ( 999999, Qt::Horizontal, QVariant() ); | ||
131 | QMap<int, QVariant> roles; | ||
132 | model->sibling ( 0, 0, QModelIndex() ); | ||
133 | model->span ( QModelIndex() ); | ||
134 | model->supportedDropActions(); | ||
135 | } | ||
136 | |||
137 | /*! | ||
138 | Tests model's implementation of QAbstractItemModel::rowCount() and hasChildren() | ||
139 | |||
140 | Models that are dynamically populated are not as fully tested here. | ||
141 | */ | ||
142 | void ModelTest::rowCount() | ||
143 | { | ||
144 | // qDebug() << "rc"; | ||
145 | // check top row | ||
146 | QModelIndex topIndex = model->index ( 0, 0, QModelIndex() ); | ||
147 | int rows = model->rowCount ( topIndex ); | ||
148 | QVERIFY( rows >= 0 ); | ||
149 | if ( rows > 0 ) | ||
150 | QVERIFY( model->hasChildren ( topIndex ) ); | ||
151 | |||
152 | QModelIndex secondLevelIndex = model->index ( 0, 0, topIndex ); | ||
153 | if ( secondLevelIndex.isValid() ) { // not the top level | ||
154 | // check a row count where parent is valid | ||
155 | rows = model->rowCount ( secondLevelIndex ); | ||
156 | QVERIFY( rows >= 0 ); | ||
157 | if ( rows > 0 ) | ||
158 | QVERIFY( model->hasChildren ( secondLevelIndex ) ); | ||
159 | } | ||
160 | |||
161 | // The models rowCount() is tested more extensively in checkChildren(), | ||
162 | // but this catches the big mistakes | ||
163 | } | ||
164 | |||
165 | /*! | ||
166 | Tests model's implementation of QAbstractItemModel::columnCount() and hasChildren() | ||
167 | */ | ||
168 | void ModelTest::columnCount() | ||
169 | { | ||
170 | // check top row | ||
171 | QModelIndex topIndex = model->index ( 0, 0, QModelIndex() ); | ||
172 | QVERIFY( model->columnCount ( topIndex ) >= 0 ); | ||
173 | |||
174 | // check a column count where parent is valid | ||
175 | QModelIndex childIndex = model->index ( 0, 0, topIndex ); | ||
176 | if ( childIndex.isValid() ) | ||
177 | QVERIFY( model->columnCount ( childIndex ) >= 0 ); | ||
178 | |||
179 | // columnCount() is tested more extensively in checkChildren(), | ||
180 | // but this catches the big mistakes | ||
181 | } | ||
182 | |||
183 | /*! | ||
184 | Tests model's implementation of QAbstractItemModel::hasIndex() | ||
185 | */ | ||
186 | void ModelTest::hasIndex() | ||
187 | { | ||
188 | // qDebug() << "hi"; | ||
189 | // Make sure that invalid values returns an invalid index | ||
190 | QVERIFY( !model->hasIndex ( -2, -2 ) ); | ||
191 | QVERIFY( !model->hasIndex ( -2, 0 ) ); | ||
192 | QVERIFY( !model->hasIndex ( 0, -2 ) ); | ||
193 | |||
194 | int rows = model->rowCount(); | ||
195 | int columns = model->columnCount(); | ||
196 | |||
197 | // check out of bounds | ||
198 | QVERIFY( !model->hasIndex ( rows, columns ) ); | ||
199 | QVERIFY( !model->hasIndex ( rows + 1, columns + 1 ) ); | ||
200 | |||
201 | if ( rows > 0 ) | ||
202 | QVERIFY( model->hasIndex ( 0, 0 ) ); | ||
203 | |||
204 | // hasIndex() is tested more extensively in checkChildren(), | ||
205 | // but this catches the big mistakes | ||
206 | } | ||
207 | |||
208 | /*! | ||
209 | Tests model's implementation of QAbstractItemModel::index() | ||
210 | */ | ||
211 | void ModelTest::index() | ||
212 | { | ||
213 | // qDebug() << "i"; | ||
214 | // Make sure that invalid values returns an invalid index | ||
215 | QVERIFY( model->index ( -2, -2 ) == QModelIndex() ); | ||
216 | QVERIFY( model->index ( -2, 0 ) == QModelIndex() ); | ||
217 | QVERIFY( model->index ( 0, -2 ) == QModelIndex() ); | ||
218 | |||
219 | int rows = model->rowCount(); | ||
220 | int columns = model->columnCount(); | ||
221 | |||
222 | if ( rows == 0 ) | ||
223 | return; | ||
224 | |||
225 | // Catch off by one errors | ||
226 | QVERIFY( model->index ( rows, columns ) == QModelIndex() ); | ||
227 | QVERIFY( model->index ( 0, 0 ).isValid() ); | ||
228 | |||
229 | // Make sure that the same index is *always* returned | ||
230 | QModelIndex a = model->index ( 0, 0 ); | ||
231 | QModelIndex b = model->index ( 0, 0 ); | ||
232 | QVERIFY( a == b ); | ||
233 | |||
234 | // index() is tested more extensively in checkChildren(), | ||
235 | // but this catches the big mistakes | ||
236 | } | ||
237 | |||
238 | /*! | ||
239 | Tests model's implementation of QAbstractItemModel::parent() | ||
240 | */ | ||
241 | void ModelTest::parent() | ||
242 | { | ||
243 | // qDebug() << "p"; | ||
244 | // Make sure the model won't crash and will return an invalid QModelIndex | ||
245 | // when asked for the parent of an invalid index. | ||
246 | QVERIFY( model->parent ( QModelIndex() ) == QModelIndex() ); | ||
247 | |||
248 | if ( model->rowCount() == 0 ) | ||
249 | return; | ||
250 | |||
251 | // Column 0 | Column 1 | | ||
252 | // QModelIndex() | | | ||
253 | // \- topIndex | topIndex1 | | ||
254 | // \- childIndex | childIndex1 | | ||
255 | |||
256 | // Common error test #1, make sure that a top level index has a parent | ||
257 | // that is a invalid QModelIndex. | ||
258 | QModelIndex topIndex = model->index ( 0, 0, QModelIndex() ); | ||
259 | QVERIFY( model->parent ( topIndex ) == QModelIndex() ); | ||
260 | |||
261 | // Common error test #2, make sure that a second level index has a parent | ||
262 | // that is the first level index. | ||
263 | if ( model->rowCount ( topIndex ) > 0 ) { | ||
264 | QModelIndex childIndex = model->index ( 0, 0, topIndex ); | ||
265 | QVERIFY( model->parent ( childIndex ) == topIndex ); | ||
266 | } | ||
267 | |||
268 | // Common error test #3, the second column should NOT have the same children | ||
269 | // as the first column in a row. | ||
270 | // Usually the second column shouldn't have children. | ||
271 | QModelIndex topIndex1 = model->index ( 0, 1, QModelIndex() ); | ||
272 | if ( model->rowCount ( topIndex1 ) > 0 ) { | ||
273 | QModelIndex childIndex = model->index ( 0, 0, topIndex ); | ||
274 | QModelIndex childIndex1 = model->index ( 0, 0, topIndex1 ); | ||
275 | QVERIFY( childIndex != childIndex1 ); | ||
276 | } | ||
277 | |||
278 | // Full test, walk n levels deep through the model making sure that all | ||
279 | // parent's children correctly specify their parent. | ||
280 | checkChildren ( QModelIndex() ); | ||
281 | } | ||
282 | |||
283 | /*! | ||
284 | Called from the parent() test. | ||
285 | |||
286 | A model that returns an index of parent X should also return X when asking | ||
287 | for the parent of the index. | ||
288 | |||
289 | This recursive function does pretty extensive testing on the whole model in an | ||
290 | effort to catch edge cases. | ||
291 | |||
292 | This function assumes that rowCount(), columnCount() and index() already work. | ||
293 | If they have a bug it will point it out, but the above tests should have already | ||
294 | found the basic bugs because it is easier to figure out the problem in | ||
295 | those tests then this one. | ||
296 | */ | ||
297 | void ModelTest::checkChildren ( const QModelIndex &parent, int currentDepth ) | ||
298 | { | ||
299 | // First just try walking back up the tree. | ||
300 | QModelIndex p = parent; | ||
301 | while ( p.isValid() ) | ||
302 | p = p.parent(); | ||
303 | |||
304 | // For models that are dynamically populated | ||
305 | if ( model->canFetchMore ( parent ) ) { | ||
306 | fetchingMore = true; | ||
307 | model->fetchMore ( parent ); | ||
308 | fetchingMore = false; | ||
309 | } | ||
310 | |||
311 | int rows = model->rowCount ( parent ); | ||
312 | int columns = model->columnCount ( parent ); | ||
313 | |||
314 | if ( rows > 0 ) | ||
315 | QVERIFY( model->hasChildren ( parent ) ); | ||
316 | |||
317 | // Some further testing against rows(), columns(), and hasChildren() | ||
318 | QVERIFY( rows >= 0 ); | ||
319 | QVERIFY( columns >= 0 ); | ||
320 | if ( rows > 0 ) | ||
321 | QVERIFY( model->hasChildren ( parent ) ); | ||
322 | |||
323 | qWarning() << "parent:" << model->data(parent).toString() << "rows:" << rows | ||
324 | << "columns:" << columns << "parent column:" << parent.column(); | ||
325 | |||
326 | const QModelIndex topLeftChild = model->index( 0, 0, parent ); | ||
327 | |||
328 | QVERIFY( !model->hasIndex ( rows + 1, 0, parent ) ); | ||
329 | for ( int r = 0; r < rows; ++r ) { | ||
330 | if ( model->canFetchMore ( parent ) ) { | ||
331 | fetchingMore = true; | ||
332 | model->fetchMore ( parent ); | ||
333 | fetchingMore = false; | ||
334 | } | ||
335 | QVERIFY( !model->hasIndex ( r, columns + 1, parent ) ); | ||
336 | for ( int c = 0; c < columns; ++c ) { | ||
337 | QVERIFY( model->hasIndex ( r, c, parent ) ); | ||
338 | QModelIndex index = model->index ( r, c, parent ); | ||
339 | // rowCount() and columnCount() said that it existed... | ||
340 | QVERIFY( index.isValid() ); | ||
341 | |||
342 | qWarning() << "\tchild("<< r <<", " << c << ", " << index.column() << "):" << model->data(index).toString(); | ||
343 | |||
344 | // index() should always return the same index when called twice in a row | ||
345 | QModelIndex modifiedIndex = model->index ( r, c, parent ); | ||
346 | QVERIFY( index == modifiedIndex ); | ||
347 | |||
348 | // Make sure we get the same index if we request it twice in a row | ||
349 | QModelIndex a = model->index ( r, c, parent ); | ||
350 | QModelIndex b = model->index ( r, c, parent ); | ||
351 | QVERIFY( a == b ); | ||
352 | |||
353 | { | ||
354 | const QModelIndex sibling = model->sibling( r, c, topLeftChild ); | ||
355 | QVERIFY( index == sibling ); | ||
356 | } | ||
357 | { | ||
358 | const QModelIndex sibling = topLeftChild.sibling( r, c ); | ||
359 | QVERIFY( index == sibling ); | ||
360 | } | ||
361 | |||
362 | // Some basic checking on the index that is returned | ||
363 | QVERIFY( index.model() == model ); | ||
364 | QCOMPARE( index.row(), r ); | ||
365 | QCOMPARE( index.column(), c ); | ||
366 | // While you can technically return a QVariant usually this is a sign | ||
367 | // of a bug in data(). Disable if this really is ok in your model. | ||
368 | // QVERIFY( model->data ( index, Qt::DisplayRole ).isValid() ); | ||
369 | |||
370 | // If the next test fails here is some somewhat useful debug you play with. | ||
371 | |||
372 | if (model->parent(index) != parent) { | ||
373 | qWarning() << r << c << currentDepth << model->data(index).toString() | ||
374 | << model->data(parent).toString(); | ||
375 | qWarning() << index << parent << model->parent(index); | ||
376 | // And a view that you can even use to show the model. | ||
377 | // QTreeView view; | ||
378 | // view.setModel(model); | ||
379 | // view.show(); | ||
380 | } | ||
381 | |||
382 | // Check that we can get back our real parent. | ||
383 | QCOMPARE( model->parent ( index ), parent ); | ||
384 | |||
385 | // recursively go down the children | ||
386 | if ( model->hasChildren ( index ) && currentDepth < 10 ) { | ||
387 | qWarning() << r << c << "has children" << model->rowCount(index); | ||
388 | checkChildren ( index, ++currentDepth ); | ||
389 | }/* else { if (currentDepth >= 10) qDebug() << "checked 10 deep"; };*/ | ||
390 | |||
391 | // make sure that after testing the children that the index doesn't change. | ||
392 | QModelIndex newerIndex = model->index ( r, c, parent ); | ||
393 | QVERIFY( index == newerIndex ); | ||
394 | } | ||
395 | } | ||
396 | } | ||
397 | |||
398 | /*! | ||
399 | Tests model's implementation of QAbstractItemModel::data() | ||
400 | */ | ||
401 | void ModelTest::data() | ||
402 | { | ||
403 | // Invalid index should return an invalid qvariant | ||
404 | QVERIFY( !model->data ( QModelIndex() ).isValid() ); | ||
405 | |||
406 | if ( model->rowCount() == 0 ) | ||
407 | return; | ||
408 | |||
409 | // A valid index should have a valid QVariant data | ||
410 | QVERIFY( model->index ( 0, 0 ).isValid() ); | ||
411 | |||
412 | // shouldn't be able to set data on an invalid index | ||
413 | QVERIFY( !model->setData ( QModelIndex(), QLatin1String ( "foo" ), Qt::DisplayRole ) ); | ||
414 | |||
415 | // General Purpose roles that should return a QString | ||
416 | QVariant variant = model->data ( model->index ( 0, 0 ), Qt::ToolTipRole ); | ||
417 | if ( variant.isValid() ) { | ||
418 | QVERIFY( variant.canConvert<QString>() ); | ||
419 | } | ||
420 | variant = model->data ( model->index ( 0, 0 ), Qt::StatusTipRole ); | ||
421 | if ( variant.isValid() ) { | ||
422 | QVERIFY( variant.canConvert<QString>() ); | ||
423 | } | ||
424 | variant = model->data ( model->index ( 0, 0 ), Qt::WhatsThisRole ); | ||
425 | if ( variant.isValid() ) { | ||
426 | QVERIFY( variant.canConvert<QString>() ); | ||
427 | } | ||
428 | |||
429 | // General Purpose roles that should return a QSize | ||
430 | variant = model->data ( model->index ( 0, 0 ), Qt::SizeHintRole ); | ||
431 | if ( variant.isValid() ) { | ||
432 | QVERIFY( variant.canConvert<QSize>() ); | ||
433 | } | ||
434 | |||
435 | // General Purpose roles that should return a QFont | ||
436 | QVariant fontVariant = model->data ( model->index ( 0, 0 ), Qt::FontRole ); | ||
437 | if ( fontVariant.isValid() ) { | ||
438 | QVERIFY( fontVariant.canConvert<QFont>() ); | ||
439 | } | ||
440 | |||
441 | // Check that the alignment is one we know about | ||
442 | QVariant textAlignmentVariant = model->data ( model->index ( 0, 0 ), Qt::TextAlignmentRole ); | ||
443 | if ( textAlignmentVariant.isValid() ) { | ||
444 | int alignment = textAlignmentVariant.toInt(); | ||
445 | QCOMPARE( alignment, ( alignment & ( Qt::AlignHorizontal_Mask | Qt::AlignVertical_Mask ) ) ); | ||
446 | } | ||
447 | |||
448 | // General Purpose roles that should return a QColor | ||
449 | QVariant colorVariant = model->data ( model->index ( 0, 0 ), Qt::BackgroundColorRole ); | ||
450 | if ( colorVariant.isValid() ) { | ||
451 | QVERIFY( colorVariant.canConvert<QColor>() ); | ||
452 | } | ||
453 | |||
454 | colorVariant = model->data ( model->index ( 0, 0 ), Qt::TextColorRole ); | ||
455 | if ( colorVariant.isValid() ) { | ||
456 | QVERIFY( colorVariant.canConvert<QColor>() ); | ||
457 | } | ||
458 | |||
459 | // Check that the "check state" is one we know about. | ||
460 | QVariant checkStateVariant = model->data ( model->index ( 0, 0 ), Qt::CheckStateRole ); | ||
461 | if ( checkStateVariant.isValid() ) { | ||
462 | int state = checkStateVariant.toInt(); | ||
463 | QVERIFY( state == Qt::Unchecked || | ||
464 | state == Qt::PartiallyChecked || | ||
465 | state == Qt::Checked ); | ||
466 | } | ||
467 | } | ||
468 | |||
469 | /*! | ||
470 | Store what is about to be inserted to make sure it actually happens | ||
471 | |||
472 | \sa rowsInserted() | ||
473 | */ | ||
474 | void ModelTest::rowsAboutToBeInserted ( const QModelIndex &parent, int start, int /* end */) | ||
475 | { | ||
476 | // Q_UNUSED(end); | ||
477 | // qDebug() << "rowsAboutToBeInserted" << "start=" << start << "end=" << end << "parent=" << model->data ( parent ).toString() | ||
478 | // << "current count of parent=" << model->rowCount ( parent ); // << "display of last=" << model->data( model->index(start-1, 0, parent) ); | ||
479 | // qDebug() << model->index(start-1, 0, parent) << model->data( model->index(start-1, 0, parent) ); | ||
480 | Changing c; | ||
481 | c.parent = parent; | ||
482 | c.oldSize = model->rowCount ( parent ); | ||
483 | c.last = model->data ( model->index ( start - 1, 0, parent ) ); | ||
484 | c.next = model->data ( model->index ( start, 0, parent ) ); | ||
485 | insert.push ( c ); | ||
486 | } | ||
487 | |||
488 | /*! | ||
489 | Confirm that what was said was going to happen actually did | ||
490 | |||
491 | \sa rowsAboutToBeInserted() | ||
492 | */ | ||
493 | void ModelTest::rowsInserted ( const QModelIndex & parent, int start, int end ) | ||
494 | { | ||
495 | Changing c = insert.pop(); | ||
496 | QVERIFY( c.parent == parent ); | ||
497 | // qDebug() << "rowsInserted" << "start=" << start << "end=" << end << "oldsize=" << c.oldSize | ||
498 | // << "parent=" << model->data ( parent ).toString() << "current rowcount of parent=" << model->rowCount ( parent ); | ||
499 | |||
500 | // for (int ii=start; ii <= end; ii++) | ||
501 | // { | ||
502 | // qDebug() << "itemWasInserted:" << ii << model->data ( model->index ( ii, 0, parent )); | ||
503 | // } | ||
504 | // qDebug(); | ||
505 | |||
506 | QVERIFY( c.oldSize + ( end - start + 1 ) == model->rowCount ( parent ) ); | ||
507 | QVERIFY( c.last == model->data ( model->index ( start - 1, 0, c.parent ) ) ); | ||
508 | |||
509 | if (c.next != model->data(model->index(end + 1, 0, c.parent))) { | ||
510 | qDebug() << start << end; | ||
511 | for (int i=0; i < model->rowCount(); ++i) | ||
512 | qDebug() << model->index(i, 0).data().toString(); | ||
513 | qDebug() << c.next << model->data(model->index(end + 1, 0, c.parent)); | ||
514 | } | ||
515 | |||
516 | QVERIFY( c.next == model->data ( model->index ( end + 1, 0, c.parent ) ) ); | ||
517 | } | ||
518 | |||
519 | void ModelTest::layoutAboutToBeChanged() | ||
520 | { | ||
521 | for ( int i = 0; i < qBound ( 0, model->rowCount(), 100 ); ++i ) | ||
522 | changing.append ( QPersistentModelIndex ( model->index ( i, 0 ) ) ); | ||
523 | } | ||
524 | |||
525 | void ModelTest::layoutChanged() | ||
526 | { | ||
527 | for ( int i = 0; i < changing.count(); ++i ) { | ||
528 | QPersistentModelIndex p = changing[i]; | ||
529 | QVERIFY( p == model->index ( p.row(), p.column(), p.parent() ) ); | ||
530 | } | ||
531 | changing.clear(); | ||
532 | } | ||
533 | |||
534 | /*! | ||
535 | Store what is about to be inserted to make sure it actually happens | ||
536 | |||
537 | \sa rowsRemoved() | ||
538 | */ | ||
539 | void ModelTest::rowsAboutToBeRemoved ( const QModelIndex &parent, int start, int end ) | ||
540 | { | ||
541 | qDebug() << "ratbr" << parent << start << end; | ||
542 | Changing c; | ||
543 | c.parent = parent; | ||
544 | c.oldSize = model->rowCount ( parent ); | ||
545 | c.last = model->data ( model->index ( start - 1, 0, parent ) ); | ||
546 | c.next = model->data ( model->index ( end + 1, 0, parent ) ); | ||
547 | remove.push ( c ); | ||
548 | } | ||
549 | |||
550 | /*! | ||
551 | Confirm that what was said was going to happen actually did | ||
552 | |||
553 | \sa rowsAboutToBeRemoved() | ||
554 | */ | ||
555 | void ModelTest::rowsRemoved ( const QModelIndex & parent, int start, int end ) | ||
556 | { | ||
557 | qDebug() << "rr" << parent << start << end; | ||
558 | Changing c = remove.pop(); | ||
559 | QVERIFY( c.parent == parent ); | ||
560 | QVERIFY( c.oldSize - ( end - start + 1 ) == model->rowCount ( parent ) ); | ||
561 | QVERIFY( c.last == model->data ( model->index ( start - 1, 0, c.parent ) ) ); | ||
562 | QVERIFY( c.next == model->data ( model->index ( start, 0, c.parent ) ) ); | ||
563 | } | ||
564 | |||
565 | void ModelTest::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) | ||
566 | { | ||
567 | QVERIFY(topLeft.isValid()); | ||
568 | QVERIFY(bottomRight.isValid()); | ||
569 | QModelIndex commonParent = bottomRight.parent(); | ||
570 | QCOMPARE(topLeft.parent(), commonParent); | ||
571 | QVERIFY(topLeft.row() <= bottomRight.row()); | ||
572 | QVERIFY(topLeft.column() <= bottomRight.column()); | ||
573 | int rowCount = model->rowCount(commonParent); | ||
574 | int columnCount = model->columnCount(commonParent); | ||
575 | QVERIFY(bottomRight.row() < rowCount); | ||
576 | QVERIFY(bottomRight.column() < columnCount); | ||
577 | } | ||
578 | |||
579 | void ModelTest::headerDataChanged(Qt::Orientation orientation, int start, int end) | ||
580 | { | ||
581 | QVERIFY(start >= 0); | ||
582 | QVERIFY(end >= 0); | ||
583 | QVERIFY(start <= end); | ||
584 | int itemCount = orientation == Qt::Vertical ? model->rowCount() : model->columnCount(); | ||
585 | QVERIFY(start < itemCount); | ||
586 | QVERIFY(end < itemCount); | ||
587 | } | ||
588 | |||
diff --git a/framework/domain/modeltest.h b/framework/domain/modeltest.h new file mode 100644 index 00000000..735a4227 --- /dev/null +++ b/framework/domain/modeltest.h | |||
@@ -0,0 +1,83 @@ | |||
1 | /**************************************************************************** | ||
2 | ** | ||
3 | ** Copyright (C) 2016 The Qt Company Ltd. | ||
4 | ** Contact: https://www.qt.io/licensing/ | ||
5 | ** | ||
6 | ** This file is part of the test suite of the Qt Toolkit. | ||
7 | ** | ||
8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ | ||
9 | ** Commercial License Usage | ||
10 | ** Licensees holding valid commercial Qt licenses may use this file in | ||
11 | ** accordance with the commercial license agreement provided with the | ||
12 | ** Software or, alternatively, in accordance with the terms contained in | ||
13 | ** a written agreement between you and The Qt Company. For licensing terms | ||
14 | ** and conditions see https://www.qt.io/terms-conditions. For further | ||
15 | ** information use the contact form at https://www.qt.io/contact-us. | ||
16 | ** | ||
17 | ** GNU General Public License Usage | ||
18 | ** Alternatively, this file may be used under the terms of the GNU | ||
19 | ** General Public License version 3 as published by the Free Software | ||
20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT | ||
21 | ** included in the packaging of this file. Please review the following | ||
22 | ** information to ensure the GNU General Public License requirements will | ||
23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. | ||
24 | ** | ||
25 | ** $QT_END_LICENSE$ | ||
26 | ** | ||
27 | ****************************************************************************/ | ||
28 | |||
29 | |||
30 | #ifndef MODELTEST_H | ||
31 | #define MODELTEST_H | ||
32 | |||
33 | #include <QtCore/QObject> | ||
34 | #include <QtCore/QAbstractItemModel> | ||
35 | #include <QtCore/QStack> | ||
36 | |||
37 | class ModelTest : public QObject | ||
38 | { | ||
39 | Q_OBJECT | ||
40 | |||
41 | public: | ||
42 | ModelTest( QAbstractItemModel *model, QObject *parent = 0 ); | ||
43 | |||
44 | private Q_SLOTS: | ||
45 | void nonDestructiveBasicTest(); | ||
46 | void rowCount(); | ||
47 | void columnCount(); | ||
48 | void hasIndex(); | ||
49 | void index(); | ||
50 | void parent(); | ||
51 | void data(); | ||
52 | |||
53 | protected Q_SLOTS: | ||
54 | void runAllTests(); | ||
55 | void layoutAboutToBeChanged(); | ||
56 | void layoutChanged(); | ||
57 | void rowsAboutToBeInserted( const QModelIndex &parent, int start, int end ); | ||
58 | void rowsInserted( const QModelIndex & parent, int start, int end ); | ||
59 | void rowsAboutToBeRemoved( const QModelIndex &parent, int start, int end ); | ||
60 | void rowsRemoved( const QModelIndex & parent, int start, int end ); | ||
61 | void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); | ||
62 | void headerDataChanged(Qt::Orientation orientation, int start, int end); | ||
63 | |||
64 | private: | ||
65 | void checkChildren( const QModelIndex &parent, int currentDepth = 0 ); | ||
66 | |||
67 | QAbstractItemModel *model; | ||
68 | |||
69 | struct Changing { | ||
70 | QModelIndex parent; | ||
71 | int oldSize; | ||
72 | QVariant last; | ||
73 | QVariant next; | ||
74 | }; | ||
75 | QStack<Changing> insert; | ||
76 | QStack<Changing> remove; | ||
77 | |||
78 | bool fetchingMore; | ||
79 | |||
80 | QList<QPersistentModelIndex> changing; | ||
81 | }; | ||
82 | |||
83 | #endif | ||