1 | // Copyright (C) 2016 The Qt Company Ltd. |
2 | // Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> |
3 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
4 | |
5 | #include "qabstractitemmodeltester.h" |
6 | |
7 | #include <private/qobject_p.h> |
8 | #include <private/qabstractitemmodel_p.h> |
9 | #include <QtCore/QPointer> |
10 | #include <QtCore/QAbstractItemModel> |
11 | #include <QtCore/QStack> |
12 | #include <QTest> |
13 | #include <QLoggingCategory> |
14 | |
15 | QT_BEGIN_NAMESPACE |
16 | |
17 | Q_LOGGING_CATEGORY(lcModelTest, "qt.modeltest" ) |
18 | |
19 | #define MODELTESTER_VERIFY(statement) \ |
20 | do { \ |
21 | if (!verify(static_cast<bool>(statement), #statement, "", __FILE__, __LINE__)) \ |
22 | return; \ |
23 | } while (false) |
24 | |
25 | #define MODELTESTER_COMPARE(actual, expected) \ |
26 | do { \ |
27 | if (!compare((actual), (expected), #actual, #expected, __FILE__, __LINE__)) \ |
28 | return; \ |
29 | } while (false) |
30 | |
31 | class QAbstractItemModelTesterPrivate : public QObjectPrivate |
32 | { |
33 | Q_DECLARE_PUBLIC(QAbstractItemModelTester) |
34 | public: |
35 | QAbstractItemModelTesterPrivate(QAbstractItemModel *model, QAbstractItemModelTester::FailureReportingMode failureReportingMode); |
36 | |
37 | void nonDestructiveBasicTest(); |
38 | void rowAndColumnCount(); |
39 | void hasIndex(); |
40 | void index(); |
41 | void parent(); |
42 | void data(); |
43 | |
44 | void runAllTests(); |
45 | |
46 | void layoutAboutToBeChanged(); |
47 | void layoutChanged(); |
48 | |
49 | void modelAboutToBeReset(); |
50 | void modelReset(); |
51 | |
52 | void columnsAboutToBeInserted(const QModelIndex &parent, int first, int last); |
53 | void columnsInserted(const QModelIndex &parent, int first, int last); |
54 | void columnsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, |
55 | const QModelIndex &destinationParent, int destinationColumn); |
56 | void columnsMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destination, |
57 | int column); |
58 | void columnsAboutToBeRemoved(const QModelIndex &parent, int first, int last); |
59 | void columnsRemoved(const QModelIndex &parent, int first, int last); |
60 | |
61 | void rowsAboutToBeInserted(const QModelIndex &parent, int start, int end); |
62 | void rowsInserted(const QModelIndex &parent, int start, int end); |
63 | void rowsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, |
64 | const QModelIndex &destinationParent, int destinationRow); |
65 | void rowsMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destination, |
66 | int row); |
67 | void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end); |
68 | void rowsRemoved(const QModelIndex &parent, int start, int end); |
69 | void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); |
70 | void headerDataChanged(Qt::Orientation orientation, int start, int end); |
71 | |
72 | private: |
73 | void checkChildren(const QModelIndex &parent, int currentDepth = 0); |
74 | |
75 | bool verify(bool statement, const char *statementStr, const char *description, const char *file, int line); |
76 | |
77 | template<typename T1, typename T2> |
78 | bool compare(const T1 &t1, const T2 &t2, |
79 | const char *actual, const char *expected, |
80 | const char *file, int line); |
81 | |
82 | QPointer<QAbstractItemModel> model; |
83 | QAbstractItemModelTester::FailureReportingMode failureReportingMode; |
84 | |
85 | struct Changing { |
86 | QModelIndex parent; |
87 | int oldSize; |
88 | QVariant last; |
89 | QVariant next; |
90 | }; |
91 | QStack<Changing> insert; |
92 | QStack<Changing> remove; |
93 | |
94 | bool useFetchMore = true; |
95 | bool fetchingMore; |
96 | |
97 | enum class ChangeInFlight { |
98 | None, |
99 | ColumnsInserted, |
100 | ColumnsMoved, |
101 | ColumnsRemoved, |
102 | LayoutChanged, |
103 | ModelReset, |
104 | RowsInserted, |
105 | RowsMoved, |
106 | RowsRemoved |
107 | }; |
108 | ChangeInFlight changeInFlight = ChangeInFlight::None; |
109 | |
110 | QList<QPersistentModelIndex> changing; |
111 | }; |
112 | |
113 | /*! |
114 | \class QAbstractItemModelTester |
115 | \since 5.11 |
116 | \inmodule QtTest |
117 | |
118 | \brief The QAbstractItemModelTester class helps testing QAbstractItemModel subclasses. |
119 | |
120 | The QAbstractItemModelTester class is a utility class to test item models. |
121 | |
122 | When implementing an item model (that is, a concrete QAbstractItemModel |
123 | subclass) one must abide to a very strict set of rules that ensure |
124 | consistency for users of the model (views, proxy models, and so on). |
125 | |
126 | For instance, for a given index, a model's reimplementation of |
127 | \l{QAbstractItemModel::hasChildren()}{hasChildren()} must be consistent |
128 | with the values returned by \l{QAbstractItemModel::rowCount()}{rowCount()} |
129 | and \l{QAbstractItemModel::columnCount()}{columnCount()}. |
130 | |
131 | QAbstractItemModelTester helps catching the most common errors in custom |
132 | item model classes. By performing a series of tests, it |
133 | will try to check that the model status is consistent at all times. The |
134 | tests will be repeated automatically every time the model is modified. |
135 | |
136 | QAbstractItemModelTester employs non-destructive tests, which typically |
137 | consist in reading data and metadata out of a given item model. |
138 | QAbstractItemModelTester will also attempt illegal modifications of |
139 | the model. In models which are properly implemented, such attempts |
140 | should be rejected, and no data should be changed as a consequence. |
141 | |
142 | \section1 Usage |
143 | |
144 | Using QAbstractItemModelTester is straightforward. In a \l{Qt Test Overview}{test case} |
145 | it is sufficient to create an instance, passing the model that |
146 | needs to be tested to the constructor: |
147 | |
148 | \code |
149 | MyModel *modelToBeTested = ...; |
150 | auto tester = new QAbstractItemModelTester(modelToBeTested); |
151 | \endcode |
152 | |
153 | QAbstractItemModelTester will report testing failures through the |
154 | Qt Test logging mechanisms. |
155 | |
156 | It is also possible to use QAbstractItemModelTester outside of a test case. |
157 | For instance, it may be useful to test an item model used by an application |
158 | without the need of building an explicit unit test for such a model (which |
159 | might be challenging). In order to use QAbstractItemModelTester outside of |
160 | a test case, pass one of the \c QAbstractItemModelTester::FailureReportingMode |
161 | enumerators to its constructor, therefore specifying how failures should |
162 | be logged. |
163 | |
164 | QAbstractItemModelTester may also report additional debugging information |
165 | as logging messages under the \c qt.modeltest logging category. Such |
166 | debug logging is disabled by default; refer to the |
167 | QLoggingCategory documentation to learn how to enable it. |
168 | |
169 | \note While QAbstractItemModelTester is a valid help for development and |
170 | testing of custom item models, it does not (and cannot) catch all possible |
171 | problems in QAbstractItemModel subclasses. Notably, it will never perform |
172 | meaningful destructive testing of a model, which must be therefore tested |
173 | separately. |
174 | |
175 | \sa {Model/View Programming}, QAbstractItemModel |
176 | */ |
177 | |
178 | /*! |
179 | \enum QAbstractItemModelTester::FailureReportingMode |
180 | |
181 | This enumeration specifies how QAbstractItemModelTester should report |
182 | a failure when it tests a QAbstractItemModel subclass. |
183 | |
184 | \value QtTest The failures will be reported as QtTest test failures. |
185 | |
186 | \value Warning The failures will be reported as |
187 | warning messages in the \c{qt.modeltest} logging category. |
188 | |
189 | \value Fatal A failure will cause immediate and |
190 | abnormal program termination. The reason for the failure will be reported |
191 | using \c{qFatal()}. |
192 | */ |
193 | |
194 | /*! |
195 | Creates a model tester instance, with the given \a parent, that will test |
196 | the model \a model. |
197 | */ |
198 | QAbstractItemModelTester::QAbstractItemModelTester(QAbstractItemModel *model, QObject *parent) |
199 | : QAbstractItemModelTester(model, FailureReportingMode::QtTest, parent) |
200 | { |
201 | } |
202 | |
203 | /*! |
204 | Creates a model tester instance, with the given \a parent, that will test |
205 | the model \a model, using the specified \a mode to report test failures. |
206 | |
207 | \sa QAbstractItemModelTester::FailureReportingMode |
208 | */ |
209 | QAbstractItemModelTester::QAbstractItemModelTester(QAbstractItemModel *model, FailureReportingMode mode, QObject *parent) |
210 | : QObject(*new QAbstractItemModelTesterPrivate(model, mode), parent) |
211 | { |
212 | if (!model) |
213 | qFatal(msg: "%s: model must not be null" , Q_FUNC_INFO); |
214 | |
215 | Q_D(QAbstractItemModelTester); |
216 | |
217 | auto runAllTests = [d] { d->runAllTests(); }; |
218 | |
219 | connect(sender: model, signal: &QAbstractItemModel::columnsAboutToBeInserted, |
220 | context: this, slot&: runAllTests); |
221 | connect(sender: model, signal: &QAbstractItemModel::columnsAboutToBeRemoved, |
222 | context: this, slot&: runAllTests); |
223 | connect(sender: model, signal: &QAbstractItemModel::columnsInserted, |
224 | context: this, slot&: runAllTests); |
225 | connect(sender: model, signal: &QAbstractItemModel::columnsRemoved, |
226 | context: this, slot&: runAllTests); |
227 | connect(sender: model, signal: &QAbstractItemModel::dataChanged, |
228 | context: this, slot&: runAllTests); |
229 | connect(sender: model, signal: &QAbstractItemModel::headerDataChanged, |
230 | context: this, slot&: runAllTests); |
231 | connect(sender: model, signal: &QAbstractItemModel::layoutAboutToBeChanged, |
232 | context: this, slot&: runAllTests); |
233 | connect(sender: model, signal: &QAbstractItemModel::layoutChanged, |
234 | context: this, slot&: runAllTests); |
235 | connect(sender: model, signal: &QAbstractItemModel::modelReset, |
236 | context: this, slot&: runAllTests); |
237 | connect(sender: model, signal: &QAbstractItemModel::rowsAboutToBeInserted, |
238 | context: this, slot&: runAllTests); |
239 | connect(sender: model, signal: &QAbstractItemModel::rowsAboutToBeRemoved, |
240 | context: this, slot&: runAllTests); |
241 | connect(sender: model, signal: &QAbstractItemModel::rowsInserted, |
242 | context: this, slot&: runAllTests); |
243 | connect(sender: model, signal: &QAbstractItemModel::rowsRemoved, |
244 | context: this, slot&: runAllTests); |
245 | |
246 | // Special checks for changes |
247 | connect(sender: model, signal: &QAbstractItemModel::layoutAboutToBeChanged, |
248 | context: this, slot: [d]{ d->layoutAboutToBeChanged(); }); |
249 | connect(sender: model, signal: &QAbstractItemModel::layoutChanged, |
250 | context: this, slot: [d]{ d->layoutChanged(); }); |
251 | |
252 | // column operations |
253 | connect(sender: model, signal: &QAbstractItemModel::columnsAboutToBeInserted, |
254 | context: this, slot: [d](const QModelIndex &parent, int start, int end) { d->columnsAboutToBeInserted(parent, first: start, last: end); }); |
255 | connect(sender: model, signal: &QAbstractItemModel::columnsAboutToBeMoved, |
256 | context: this, slot: [d](const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationColumn) { |
257 | d->columnsAboutToBeMoved(sourceParent, sourceStart, sourceEnd, destinationParent, destinationColumn); }); |
258 | connect(sender: model, signal: &QAbstractItemModel::columnsAboutToBeRemoved, |
259 | context: this, slot: [d](const QModelIndex &parent, int start, int end) { d->columnsAboutToBeRemoved(parent, first: start, last: end); }); |
260 | connect(sender: model, signal: &QAbstractItemModel::columnsInserted, |
261 | context: this, slot: [d](const QModelIndex &parent, int start, int end) { d->columnsInserted(parent, first: start, last: end); }); |
262 | connect(sender: model, signal: &QAbstractItemModel::columnsMoved, |
263 | context: this, slot: [d](const QModelIndex &parent, int start, int end, const QModelIndex &destination, int col) { |
264 | d->columnsMoved(parent, start, end, destination, column: col); }); |
265 | connect(sender: model, signal: &QAbstractItemModel::columnsRemoved, |
266 | context: this, slot: [d](const QModelIndex &parent, int start, int end) { d->columnsRemoved(parent, first: start, last: end); }); |
267 | |
268 | // row operations |
269 | connect(sender: model, signal: &QAbstractItemModel::rowsAboutToBeInserted, |
270 | context: this, slot: [d](const QModelIndex &parent, int start, int end) { d->rowsAboutToBeInserted(parent, start, end); }); |
271 | connect(sender: model, signal: &QAbstractItemModel::rowsAboutToBeMoved, |
272 | context: this, slot: [d](const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow) { |
273 | d->rowsAboutToBeMoved(sourceParent, sourceStart, sourceEnd, destinationParent, destinationRow); }); |
274 | connect(sender: model, signal: &QAbstractItemModel::rowsAboutToBeRemoved, |
275 | context: this, slot: [d](const QModelIndex &parent, int start, int end) { d->rowsAboutToBeRemoved(parent, start, end); }); |
276 | connect(sender: model, signal: &QAbstractItemModel::rowsInserted, |
277 | context: this, slot: [d](const QModelIndex &parent, int start, int end) { d->rowsInserted(parent, start, end); }); |
278 | connect(sender: model, signal: &QAbstractItemModel::rowsMoved, |
279 | context: this, slot: [d](const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row) { |
280 | d->rowsMoved(parent, start, end, destination, row); }); |
281 | connect(sender: model, signal: &QAbstractItemModel::rowsRemoved, |
282 | context: this, slot: [d](const QModelIndex &parent, int start, int end) { d->rowsRemoved(parent, start, end); }); |
283 | |
284 | // reset |
285 | connect(sender: model, signal: &QAbstractItemModel::modelAboutToBeReset, |
286 | context: this, slot: [d]() { d->modelAboutToBeReset(); }); |
287 | connect(sender: model, signal: &QAbstractItemModel::modelReset, |
288 | context: this, slot: [d]() { d->modelReset(); }); |
289 | |
290 | // data |
291 | connect(sender: model, signal: &QAbstractItemModel::dataChanged, |
292 | context: this, slot: [d](const QModelIndex &topLeft, const QModelIndex &bottomRight) { d->dataChanged(topLeft, bottomRight); }); |
293 | connect(sender: model, signal: &QAbstractItemModel::headerDataChanged, |
294 | context: this, slot: [d](Qt::Orientation orientation, int start, int end) { d->headerDataChanged(orientation, start, end); }); |
295 | |
296 | runAllTests(); |
297 | } |
298 | |
299 | /*! |
300 | Returns the model that this instance is testing. |
301 | */ |
302 | QAbstractItemModel *QAbstractItemModelTester::model() const |
303 | { |
304 | Q_D(const QAbstractItemModelTester); |
305 | return d->model.data(); |
306 | } |
307 | |
308 | /*! |
309 | Returns the mode that this instancing is using to report test failures. |
310 | |
311 | \sa QAbstractItemModelTester::FailureReportingMode |
312 | */ |
313 | QAbstractItemModelTester::FailureReportingMode QAbstractItemModelTester::failureReportingMode() const |
314 | { |
315 | Q_D(const QAbstractItemModelTester); |
316 | return d->failureReportingMode; |
317 | } |
318 | |
319 | /*! |
320 | If \a value is true, enables dynamic population of the |
321 | tested model, which is the default. |
322 | If \a value is false, it disables it. |
323 | |
324 | \since 6.4 |
325 | \sa QAbstractItemModel::fetchMore() |
326 | */ |
327 | void QAbstractItemModelTester::setUseFetchMore(bool value) |
328 | { |
329 | Q_D(QAbstractItemModelTester); |
330 | d->useFetchMore = value; |
331 | } |
332 | |
333 | bool QAbstractItemModelTester::verify(bool statement, const char *statementStr, const char *description, const char *file, int line) |
334 | { |
335 | Q_D(QAbstractItemModelTester); |
336 | return d->verify(statement, statementStr, description, file, line); |
337 | } |
338 | |
339 | QAbstractItemModelTesterPrivate::QAbstractItemModelTesterPrivate(QAbstractItemModel *model, QAbstractItemModelTester::FailureReportingMode failureReportingMode) |
340 | : model(model), |
341 | failureReportingMode(failureReportingMode), |
342 | fetchingMore(false) |
343 | { |
344 | } |
345 | |
346 | void QAbstractItemModelTesterPrivate::runAllTests() |
347 | { |
348 | if (fetchingMore) |
349 | return; |
350 | nonDestructiveBasicTest(); |
351 | rowAndColumnCount(); |
352 | hasIndex(); |
353 | index(); |
354 | parent(); |
355 | data(); |
356 | } |
357 | |
358 | /* |
359 | nonDestructiveBasicTest tries to call a number of the basic functions (not all) |
360 | to make sure the model doesn't outright segfault, testing the functions that makes sense. |
361 | */ |
362 | void QAbstractItemModelTesterPrivate::nonDestructiveBasicTest() |
363 | { |
364 | MODELTESTER_VERIFY(!model->buddy(QModelIndex()).isValid()); |
365 | model->canFetchMore(parent: QModelIndex()); |
366 | MODELTESTER_VERIFY(model->columnCount(QModelIndex()) >= 0); |
367 | if (useFetchMore) { |
368 | fetchingMore = true; |
369 | model->fetchMore(parent: QModelIndex()); |
370 | fetchingMore = false; |
371 | } |
372 | Qt::ItemFlags flags = model->flags(index: QModelIndex()); |
373 | MODELTESTER_VERIFY(flags == Qt::ItemIsDropEnabled || flags == 0); |
374 | model->hasChildren(parent: QModelIndex()); |
375 | const bool hasRow = model->hasIndex(row: 0, column: 0); |
376 | QVariant cache; |
377 | if (hasRow) |
378 | model->match(start: model->index(row: 0, column: 0), role: -1, value: cache); |
379 | model->mimeTypes(); |
380 | MODELTESTER_VERIFY(!model->parent(QModelIndex()).isValid()); |
381 | MODELTESTER_VERIFY(model->rowCount() >= 0); |
382 | model->span(index: QModelIndex()); |
383 | model->supportedDropActions(); |
384 | model->roleNames(); |
385 | } |
386 | |
387 | /* |
388 | Tests model's implementation of QAbstractItemModel::rowCount(), |
389 | columnCount() and hasChildren(). |
390 | |
391 | Models that are dynamically populated are not as fully tested here. |
392 | */ |
393 | void QAbstractItemModelTesterPrivate::rowAndColumnCount() |
394 | { |
395 | if (!model->hasChildren()) |
396 | return; |
397 | |
398 | QModelIndex topIndex = model->index(row: 0, column: 0, parent: QModelIndex()); |
399 | |
400 | // check top row |
401 | int rows = model->rowCount(parent: topIndex); |
402 | MODELTESTER_VERIFY(rows >= 0); |
403 | |
404 | int columns = model->columnCount(parent: topIndex); |
405 | MODELTESTER_VERIFY(columns >= 0); |
406 | |
407 | if (rows == 0 || columns == 0) |
408 | return; |
409 | |
410 | MODELTESTER_VERIFY(model->hasChildren(topIndex)); |
411 | |
412 | QModelIndex secondLevelIndex = model->index(row: 0, column: 0, parent: topIndex); |
413 | MODELTESTER_VERIFY(secondLevelIndex.isValid()); |
414 | |
415 | rows = model->rowCount(parent: secondLevelIndex); |
416 | MODELTESTER_VERIFY(rows >= 0); |
417 | |
418 | columns = model->columnCount(parent: secondLevelIndex); |
419 | MODELTESTER_VERIFY(columns >= 0); |
420 | |
421 | if (rows == 0 || columns == 0) |
422 | return; |
423 | |
424 | MODELTESTER_VERIFY(model->hasChildren(secondLevelIndex)); |
425 | |
426 | // rowCount() / columnCount() are tested more extensively in checkChildren() |
427 | } |
428 | |
429 | /* |
430 | Tests model's implementation of QAbstractItemModel::hasIndex() |
431 | */ |
432 | void QAbstractItemModelTesterPrivate::hasIndex() |
433 | { |
434 | // Make sure that invalid values returns an invalid index |
435 | MODELTESTER_VERIFY(!model->hasIndex(-2, -2)); |
436 | MODELTESTER_VERIFY(!model->hasIndex(-2, 0)); |
437 | MODELTESTER_VERIFY(!model->hasIndex(0, -2)); |
438 | |
439 | const int rows = model->rowCount(); |
440 | const int columns = model->columnCount(); |
441 | |
442 | // check out of bounds |
443 | MODELTESTER_VERIFY(!model->hasIndex(rows, columns)); |
444 | MODELTESTER_VERIFY(!model->hasIndex(rows + 1, columns + 1)); |
445 | |
446 | if (rows > 0 && columns > 0) |
447 | MODELTESTER_VERIFY(model->hasIndex(0, 0)); |
448 | |
449 | // hasIndex() is tested more extensively in checkChildren(), |
450 | // but this catches the big mistakes |
451 | } |
452 | |
453 | /* |
454 | Tests model's implementation of QAbstractItemModel::index() |
455 | */ |
456 | void QAbstractItemModelTesterPrivate::index() |
457 | { |
458 | const int rows = model->rowCount(); |
459 | const int columns = model->columnCount(); |
460 | |
461 | for (int row = 0; row < rows; ++row) { |
462 | for (int column = 0; column < columns; ++column) { |
463 | // Make sure that the same index is *always* returned |
464 | QModelIndex a = model->index(row, column); |
465 | QModelIndex b = model->index(row, column); |
466 | MODELTESTER_VERIFY(a.isValid()); |
467 | MODELTESTER_VERIFY(b.isValid()); |
468 | MODELTESTER_COMPARE(a, b); |
469 | } |
470 | } |
471 | |
472 | // index() is tested more extensively in checkChildren(), |
473 | // but this catches the big mistakes |
474 | } |
475 | |
476 | /* |
477 | Tests model's implementation of QAbstractItemModel::parent() |
478 | */ |
479 | void QAbstractItemModelTesterPrivate::parent() |
480 | { |
481 | // Make sure the model won't crash and will return an invalid QModelIndex |
482 | // when asked for the parent of an invalid index. |
483 | MODELTESTER_VERIFY(!model->parent(QModelIndex()).isValid()); |
484 | |
485 | if (model->rowCount() == 0 || model->columnCount() == 0) |
486 | return; |
487 | |
488 | // Column 0 | Column 1 | |
489 | // QModelIndex() | | |
490 | // \- topIndex | topIndex1 | |
491 | // \- childIndex | childIndex1 | |
492 | |
493 | // Common error test #1, make sure that a top level index has a parent |
494 | // that is a invalid QModelIndex. |
495 | QModelIndex topIndex = model->index(row: 0, column: 0, parent: QModelIndex()); |
496 | MODELTESTER_VERIFY(topIndex.isValid()); |
497 | MODELTESTER_VERIFY(!model->parent(topIndex).isValid()); |
498 | |
499 | // Common error test #2, make sure that a second level index has a parent |
500 | // that is the first level index. |
501 | if (model->rowCount(parent: topIndex) > 0 && model->columnCount(parent: topIndex) > 0) { |
502 | QModelIndex childIndex = model->index(row: 0, column: 0, parent: topIndex); |
503 | MODELTESTER_VERIFY(childIndex.isValid()); |
504 | MODELTESTER_COMPARE(model->parent(childIndex), topIndex); |
505 | } |
506 | |
507 | // Common error test #3, the second column should NOT have the same children |
508 | // as the first column in a row. |
509 | // Usually the second column shouldn't have children. |
510 | if (model->hasIndex(row: 0, column: 1)) { |
511 | QModelIndex topIndex1 = model->index(row: 0, column: 1, parent: QModelIndex()); |
512 | MODELTESTER_VERIFY(topIndex1.isValid()); |
513 | if (model->rowCount(parent: topIndex) > 0 && model->rowCount(parent: topIndex1) > 0) { |
514 | QModelIndex childIndex = model->index(row: 0, column: 0, parent: topIndex); |
515 | MODELTESTER_VERIFY(childIndex.isValid()); |
516 | QModelIndex childIndex1 = model->index(row: 0, column: 0, parent: topIndex1); |
517 | MODELTESTER_VERIFY(childIndex1.isValid()); |
518 | MODELTESTER_VERIFY(childIndex != childIndex1); |
519 | } |
520 | } |
521 | |
522 | // Full test, walk n levels deep through the model making sure that all |
523 | // parent's children correctly specify their parent. |
524 | checkChildren(parent: QModelIndex()); |
525 | } |
526 | |
527 | /* |
528 | Called from the parent() test. |
529 | |
530 | A model that returns an index of parent X should also return X when asking |
531 | for the parent of the index. |
532 | |
533 | This recursive function does pretty extensive testing on the whole model in an |
534 | effort to catch edge cases. |
535 | |
536 | This function assumes that rowCount(), columnCount() and index() already work. |
537 | If they have a bug it will point it out, but the above tests should have already |
538 | found the basic bugs because it is easier to figure out the problem in |
539 | those tests then this one. |
540 | */ |
541 | void QAbstractItemModelTesterPrivate::checkChildren(const QModelIndex &parent, int currentDepth) |
542 | { |
543 | // First just try walking back up the tree. |
544 | QModelIndex p = parent; |
545 | while (p.isValid()) |
546 | p = p.parent(); |
547 | |
548 | // For models that are dynamically populated |
549 | if (model->canFetchMore(parent) && useFetchMore) { |
550 | fetchingMore = true; |
551 | model->fetchMore(parent); |
552 | fetchingMore = false; |
553 | } |
554 | |
555 | const int rows = model->rowCount(parent); |
556 | const int columns = model->columnCount(parent); |
557 | |
558 | if (rows > 0) |
559 | MODELTESTER_VERIFY(model->hasChildren(parent)); |
560 | |
561 | // Some further testing against rows(), columns(), and hasChildren() |
562 | MODELTESTER_VERIFY(rows >= 0); |
563 | MODELTESTER_VERIFY(columns >= 0); |
564 | if (rows > 0 && columns > 0) |
565 | MODELTESTER_VERIFY(model->hasChildren(parent)); |
566 | |
567 | const QModelIndex topLeftChild = model->index(row: 0, column: 0, parent); |
568 | |
569 | MODELTESTER_VERIFY(!model->hasIndex(rows, 0, parent)); |
570 | MODELTESTER_VERIFY(!model->hasIndex(rows + 1, 0, parent)); |
571 | |
572 | for (int r = 0; r < rows; ++r) { |
573 | MODELTESTER_VERIFY(!model->hasIndex(r, columns, parent)); |
574 | MODELTESTER_VERIFY(!model->hasIndex(r, columns + 1, parent)); |
575 | for (int c = 0; c < columns; ++c) { |
576 | MODELTESTER_VERIFY(model->hasIndex(r, c, parent)); |
577 | QModelIndex index = model->index(row: r, column: c, parent); |
578 | // rowCount() and columnCount() said that it existed... |
579 | if (!index.isValid()) |
580 | qCWarning(lcModelTest) << "Got invalid index at row=" << r << "col=" << c << "parent=" << parent; |
581 | MODELTESTER_VERIFY(index.isValid()); |
582 | |
583 | // index() should always return the same index when called twice in a row |
584 | QModelIndex modifiedIndex = model->index(row: r, column: c, parent); |
585 | MODELTESTER_COMPARE(index, modifiedIndex); |
586 | |
587 | { |
588 | const QModelIndex sibling = model->sibling(row: r, column: c, idx: topLeftChild); |
589 | MODELTESTER_COMPARE(index, sibling); |
590 | } |
591 | { |
592 | const QModelIndex sibling = topLeftChild.sibling(arow: r, acolumn: c); |
593 | MODELTESTER_COMPARE(index, sibling); |
594 | } |
595 | |
596 | // Some basic checking on the index that is returned |
597 | MODELTESTER_COMPARE(index.model(), model); |
598 | MODELTESTER_COMPARE(index.row(), r); |
599 | MODELTESTER_COMPARE(index.column(), c); |
600 | |
601 | // If the next test fails here is some somewhat useful debug you play with. |
602 | if (model->parent(child: index) != parent) { |
603 | qCWarning(lcModelTest) << "Inconsistent parent() implementation detected:" ; |
604 | qCWarning(lcModelTest) << " index=" << index << "exp. parent=" << parent << "act. parent=" << model->parent(child: index); |
605 | qCWarning(lcModelTest) << " row=" << r << "col=" << c << "depth=" << currentDepth; |
606 | qCWarning(lcModelTest) << " data for child" << model->data(index).toString(); |
607 | qCWarning(lcModelTest) << " data for parent" << model->data(index: parent).toString(); |
608 | } |
609 | |
610 | // Check that we can get back our real parent. |
611 | MODELTESTER_COMPARE(model->parent(index), parent); |
612 | |
613 | QPersistentModelIndex persistentIndex = index; |
614 | |
615 | // recursively go down the children |
616 | if (model->hasChildren(parent: index) && currentDepth < 10) |
617 | checkChildren(parent: index, currentDepth: currentDepth + 1); |
618 | |
619 | // make sure that after testing the children that the index doesn't change. |
620 | QModelIndex newerIndex = model->index(row: r, column: c, parent); |
621 | MODELTESTER_COMPARE(persistentIndex, newerIndex); |
622 | } |
623 | } |
624 | } |
625 | |
626 | /* |
627 | Tests model's implementation of QAbstractItemModel::data() |
628 | */ |
629 | void QAbstractItemModelTesterPrivate::data() |
630 | { |
631 | if (model->rowCount() == 0 || model->columnCount() == 0) |
632 | return; |
633 | |
634 | MODELTESTER_VERIFY(model->index(0, 0).isValid()); |
635 | |
636 | // General Purpose roles that should return a QString |
637 | QVariant variant; |
638 | variant = model->data(index: model->index(row: 0, column: 0), role: Qt::DisplayRole); |
639 | if (variant.isValid()) |
640 | MODELTESTER_VERIFY(variant.canConvert<QString>()); |
641 | variant = model->data(index: model->index(row: 0, column: 0), role: Qt::ToolTipRole); |
642 | if (variant.isValid()) |
643 | MODELTESTER_VERIFY(variant.canConvert<QString>()); |
644 | variant = model->data(index: model->index(row: 0, column: 0), role: Qt::StatusTipRole); |
645 | if (variant.isValid()) |
646 | MODELTESTER_VERIFY(variant.canConvert<QString>()); |
647 | variant = model->data(index: model->index(row: 0, column: 0), role: Qt::WhatsThisRole); |
648 | if (variant.isValid()) |
649 | MODELTESTER_VERIFY(variant.canConvert<QString>()); |
650 | |
651 | // General Purpose roles that should return a QSize |
652 | variant = model->data(index: model->index(row: 0, column: 0), role: Qt::SizeHintRole); |
653 | if (variant.isValid()) |
654 | MODELTESTER_VERIFY(variant.canConvert<QSize>()); |
655 | |
656 | // Check that the alignment is one we know about |
657 | QVariant textAlignmentVariant = model->data(index: model->index(row: 0, column: 0), role: Qt::TextAlignmentRole); |
658 | if (textAlignmentVariant.isValid()) { |
659 | Qt::Alignment alignment = QtPrivate::legacyFlagValueFromModelData<Qt::Alignment>(data: textAlignmentVariant); |
660 | MODELTESTER_COMPARE(alignment, (alignment & (Qt::AlignHorizontal_Mask | Qt::AlignVertical_Mask))); |
661 | } |
662 | |
663 | // Check that the "check state" is one we know about. |
664 | QVariant checkStateVariant = model->data(index: model->index(row: 0, column: 0), role: Qt::CheckStateRole); |
665 | if (checkStateVariant.isValid()) { |
666 | Qt::CheckState state = QtPrivate::legacyEnumValueFromModelData<Qt::CheckState>(data: checkStateVariant); |
667 | MODELTESTER_VERIFY(state == Qt::Unchecked |
668 | || state == Qt::PartiallyChecked |
669 | || state == Qt::Checked); |
670 | } |
671 | |
672 | Q_Q(QAbstractItemModelTester); |
673 | |
674 | if (!QTestPrivate::testDataGuiRoles(tester: q)) |
675 | return; |
676 | } |
677 | |
678 | void QAbstractItemModelTesterPrivate::columnsAboutToBeInserted(const QModelIndex &parent, int start, |
679 | int end) |
680 | { |
681 | MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::None); |
682 | changeInFlight = ChangeInFlight::ColumnsInserted; |
683 | |
684 | qCDebug(lcModelTest) << "columnsAboutToBeInserted" |
685 | << "start=" << start << "end=" << end << "parent=" << parent |
686 | << "parent data=" << model->data(index: parent).toString() |
687 | << "current count of parent=" << model->rowCount(parent) |
688 | << "last before insertion=" << model->index(row: start - 1, column: 0, parent) |
689 | << model->data(index: model->index(row: start - 1, column: 0, parent)); |
690 | } |
691 | |
692 | void QAbstractItemModelTesterPrivate::columnsInserted(const QModelIndex &parent, int first, |
693 | int last) |
694 | { |
695 | MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::ColumnsInserted); |
696 | changeInFlight = ChangeInFlight::None; |
697 | |
698 | qCDebug(lcModelTest) << "columnsInserted" |
699 | << "start=" << first << "end=" << last << "parent=" << parent |
700 | << "parent data=" << model->data(index: parent).toString() |
701 | << "current count of parent=" << model->rowCount(parent); |
702 | } |
703 | |
704 | void QAbstractItemModelTesterPrivate::columnsAboutToBeMoved(const QModelIndex &sourceParent, |
705 | int sourceStart, int sourceEnd, |
706 | const QModelIndex &destinationParent, |
707 | int destinationColumn) |
708 | { |
709 | MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::None); |
710 | changeInFlight = ChangeInFlight::ColumnsMoved; |
711 | |
712 | qCDebug(lcModelTest) << "columnsAboutToBeMoved" |
713 | << "sourceStart=" << sourceStart << "sourceEnd=" << sourceEnd |
714 | << "sourceParent=" << sourceParent |
715 | << "sourceParent data=" << model->data(index: sourceParent).toString() |
716 | << "destinationParent=" << destinationParent |
717 | << "destinationColumn=" << destinationColumn; |
718 | } |
719 | |
720 | void QAbstractItemModelTesterPrivate::columnsMoved(const QModelIndex &sourceParent, int sourceStart, |
721 | int sourceEnd, |
722 | const QModelIndex &destinationParent, |
723 | int destinationColumn) |
724 | { |
725 | MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::ColumnsMoved); |
726 | changeInFlight = ChangeInFlight::None; |
727 | |
728 | qCDebug(lcModelTest) << "columnsMoved" |
729 | << "sourceStart=" << sourceStart << "sourceEnd=" << sourceEnd |
730 | << "sourceParent=" << sourceParent |
731 | << "sourceParent data=" << model->data(index: sourceParent).toString() |
732 | << "destinationParent=" << destinationParent |
733 | << "destinationColumn=" << destinationColumn; |
734 | } |
735 | |
736 | void QAbstractItemModelTesterPrivate::columnsAboutToBeRemoved(const QModelIndex &parent, int first, |
737 | int last) |
738 | { |
739 | MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::None); |
740 | changeInFlight = ChangeInFlight::ColumnsRemoved; |
741 | |
742 | qCDebug(lcModelTest) << "columnsAboutToBeRemoved" |
743 | << "start=" << first << "end=" << last << "parent=" << parent |
744 | << "parent data=" << model->data(index: parent).toString() |
745 | << "current count of parent=" << model->rowCount(parent) |
746 | << "last before removal=" << model->index(row: first - 1, column: 0, parent) |
747 | << model->data(index: model->index(row: first - 1, column: 0, parent)); |
748 | } |
749 | |
750 | void QAbstractItemModelTesterPrivate::columnsRemoved(const QModelIndex &parent, int first, int last) |
751 | { |
752 | MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::ColumnsRemoved); |
753 | changeInFlight = ChangeInFlight::None; |
754 | |
755 | qCDebug(lcModelTest) << "columnsRemoved" |
756 | << "start=" << first << "end=" << last << "parent=" << parent |
757 | << "parent data=" << model->data(index: parent).toString() |
758 | << "current count of parent=" << model->rowCount(parent); |
759 | } |
760 | |
761 | /* |
762 | Store what is about to be inserted to make sure it actually happens |
763 | |
764 | \sa rowsInserted() |
765 | */ |
766 | void QAbstractItemModelTesterPrivate::rowsAboutToBeInserted(const QModelIndex &parent, int start, int end) |
767 | { |
768 | MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::None); |
769 | changeInFlight = ChangeInFlight::RowsInserted; |
770 | |
771 | qCDebug(lcModelTest) << "rowsAboutToBeInserted" |
772 | << "start=" << start << "end=" << end << "parent=" << parent |
773 | << "parent data=" << model->data(index: parent).toString() |
774 | << "current count of parent=" << model->rowCount(parent) |
775 | << "last before insertion=" << model->index(row: start - 1, column: 0, parent) << model->data(index: model->index(row: start - 1, column: 0, parent)); |
776 | |
777 | Changing c; |
778 | c.parent = parent; |
779 | c.oldSize = model->rowCount(parent); |
780 | c.last = (start - 1 >= 0) ? model->index(row: start - 1, column: 0, parent).data() : QVariant(); |
781 | c.next = (start < c.oldSize) ? model->index(row: start, column: 0, parent).data() : QVariant(); |
782 | insert.push(t: c); |
783 | } |
784 | |
785 | /* |
786 | Confirm that what was said was going to happen actually did |
787 | |
788 | \sa rowsAboutToBeInserted() |
789 | */ |
790 | void QAbstractItemModelTesterPrivate::rowsInserted(const QModelIndex &parent, int start, int end) |
791 | { |
792 | MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::RowsInserted); |
793 | changeInFlight = ChangeInFlight::None; |
794 | |
795 | qCDebug(lcModelTest) << "rowsInserted" |
796 | << "start=" << start << "end=" << end << "parent=" << parent |
797 | << "parent data=" << model->data(index: parent).toString() |
798 | << "current count of parent=" << model->rowCount(parent); |
799 | |
800 | for (int i = start; i <= end; ++i) { |
801 | qCDebug(lcModelTest) << " itemWasInserted:" << i |
802 | << model->index(row: i, column: 0, parent).data(); |
803 | } |
804 | |
805 | |
806 | Changing c = insert.pop(); |
807 | MODELTESTER_COMPARE(parent, c.parent); |
808 | |
809 | MODELTESTER_COMPARE(model->rowCount(parent), c.oldSize + (end - start + 1)); |
810 | if (start - 1 >= 0) |
811 | MODELTESTER_COMPARE(model->data(model->index(start - 1, 0, c.parent)), c.last); |
812 | |
813 | if (end + 1 < model->rowCount(parent: c.parent)) { |
814 | if (c.next != model->data(index: model->index(row: end + 1, column: 0, parent: c.parent))) { |
815 | qDebug() << start << end; |
816 | for (int i = 0; i < model->rowCount(); ++i) |
817 | qDebug() << model->index(row: i, column: 0).data().toString(); |
818 | qDebug() << c.next << model->data(index: model->index(row: end + 1, column: 0, parent: c.parent)); |
819 | } |
820 | |
821 | MODELTESTER_COMPARE(model->data(model->index(end + 1, 0, c.parent)), c.next); |
822 | } |
823 | } |
824 | |
825 | void QAbstractItemModelTesterPrivate::rowsAboutToBeMoved(const QModelIndex &sourceParent, |
826 | int sourceStart, int sourceEnd, |
827 | const QModelIndex &destinationParent, |
828 | int destinationRow) |
829 | { |
830 | MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::None); |
831 | changeInFlight = ChangeInFlight::RowsMoved; |
832 | |
833 | qCDebug(lcModelTest) << "rowsAboutToBeMoved" |
834 | << "sourceStart=" << sourceStart << "sourceEnd=" << sourceEnd |
835 | << "sourceParent=" << sourceParent |
836 | << "sourceParent data=" << model->data(index: sourceParent).toString() |
837 | << "destinationParent=" << destinationParent |
838 | << "destinationRow=" << destinationRow; |
839 | } |
840 | |
841 | void QAbstractItemModelTesterPrivate::rowsMoved(const QModelIndex &sourceParent, int sourceStart, |
842 | int sourceEnd, const QModelIndex &destinationParent, |
843 | int destinationRow) |
844 | { |
845 | MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::RowsMoved); |
846 | changeInFlight = ChangeInFlight::None; |
847 | |
848 | qCDebug(lcModelTest) << "rowsMoved" |
849 | << "sourceStart=" << sourceStart << "sourceEnd=" << sourceEnd |
850 | << "sourceParent=" << sourceParent |
851 | << "sourceParent data=" << model->data(index: sourceParent).toString() |
852 | << "destinationParent=" << destinationParent |
853 | << "destinationRow=" << destinationRow; |
854 | } |
855 | |
856 | void QAbstractItemModelTesterPrivate::layoutAboutToBeChanged() |
857 | { |
858 | MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::None); |
859 | changeInFlight = ChangeInFlight::LayoutChanged; |
860 | |
861 | for (int i = 0; i < qBound(min: 0, val: model->rowCount(), max: 100); ++i) |
862 | changing.append(t: QPersistentModelIndex(model->index(row: i, column: 0))); |
863 | } |
864 | |
865 | void QAbstractItemModelTesterPrivate::layoutChanged() |
866 | { |
867 | MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::LayoutChanged); |
868 | changeInFlight = ChangeInFlight::None; |
869 | |
870 | for (int i = 0; i < changing.size(); ++i) { |
871 | QPersistentModelIndex p = changing[i]; |
872 | MODELTESTER_COMPARE(model->index(p.row(), p.column(), p.parent()), QModelIndex(p)); |
873 | } |
874 | changing.clear(); |
875 | } |
876 | |
877 | void QAbstractItemModelTesterPrivate::modelAboutToBeReset() |
878 | { |
879 | MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::None); |
880 | changeInFlight = ChangeInFlight::ModelReset; |
881 | } |
882 | |
883 | void QAbstractItemModelTesterPrivate::modelReset() |
884 | { |
885 | MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::ModelReset); |
886 | changeInFlight = ChangeInFlight::None; |
887 | } |
888 | |
889 | /* |
890 | Store what is about to be inserted to make sure it actually happens |
891 | |
892 | \sa rowsRemoved() |
893 | */ |
894 | void QAbstractItemModelTesterPrivate::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) |
895 | { |
896 | MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::None); |
897 | changeInFlight = ChangeInFlight::RowsRemoved; |
898 | |
899 | qCDebug(lcModelTest) << "rowsAboutToBeRemoved" |
900 | << "start=" << start << "end=" << end << "parent=" << parent |
901 | << "parent data=" << model->data(index: parent).toString() |
902 | << "current count of parent=" << model->rowCount(parent) |
903 | << "last before removal=" << model->index(row: start - 1, column: 0, parent) << model->data(index: model->index(row: start - 1, column: 0, parent)); |
904 | |
905 | Changing c; |
906 | c.parent = parent; |
907 | c.oldSize = model->rowCount(parent); |
908 | if (start > 0 && model->columnCount(parent) > 0) { |
909 | const QModelIndex startIndex = model->index(row: start - 1, column: 0, parent); |
910 | MODELTESTER_VERIFY(startIndex.isValid()); |
911 | c.last = model->data(index: startIndex); |
912 | } |
913 | if (end < c.oldSize - 1 && model->columnCount(parent) > 0) { |
914 | const QModelIndex endIndex = model->index(row: end + 1, column: 0, parent); |
915 | MODELTESTER_VERIFY(endIndex.isValid()); |
916 | c.next = model->data(index: endIndex); |
917 | } |
918 | |
919 | remove.push(t: c); |
920 | } |
921 | |
922 | /* |
923 | Confirm that what was said was going to happen actually did |
924 | |
925 | \sa rowsAboutToBeRemoved() |
926 | */ |
927 | void QAbstractItemModelTesterPrivate::rowsRemoved(const QModelIndex &parent, int start, int end) |
928 | { |
929 | MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::RowsRemoved); |
930 | changeInFlight = ChangeInFlight::None; |
931 | |
932 | qCDebug(lcModelTest) << "rowsRemoved" |
933 | << "start=" << start << "end=" << end << "parent=" << parent |
934 | << "parent data=" << model->data(index: parent).toString() |
935 | << "current count of parent=" << model->rowCount(parent); |
936 | |
937 | Changing c = remove.pop(); |
938 | MODELTESTER_COMPARE(parent, c.parent); |
939 | MODELTESTER_COMPARE(model->rowCount(parent), c.oldSize - (end - start + 1)); |
940 | if (start > 0) |
941 | MODELTESTER_COMPARE(model->data(model->index(start - 1, 0, c.parent)), c.last); |
942 | if (end < c.oldSize - 1) |
943 | MODELTESTER_COMPARE(model->data(model->index(start, 0, c.parent)), c.next); |
944 | } |
945 | |
946 | void QAbstractItemModelTesterPrivate::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) |
947 | { |
948 | MODELTESTER_VERIFY(topLeft.isValid()); |
949 | MODELTESTER_VERIFY(bottomRight.isValid()); |
950 | QModelIndex commonParent = bottomRight.parent(); |
951 | MODELTESTER_COMPARE(topLeft.parent(), commonParent); |
952 | MODELTESTER_VERIFY(topLeft.row() <= bottomRight.row()); |
953 | MODELTESTER_VERIFY(topLeft.column() <= bottomRight.column()); |
954 | int rowCount = model->rowCount(parent: commonParent); |
955 | int columnCount = model->columnCount(parent: commonParent); |
956 | MODELTESTER_VERIFY(bottomRight.row() < rowCount); |
957 | MODELTESTER_VERIFY(bottomRight.column() < columnCount); |
958 | } |
959 | |
960 | void QAbstractItemModelTesterPrivate::(Qt::Orientation orientation, int start, int end) |
961 | { |
962 | MODELTESTER_VERIFY(start >= 0); |
963 | MODELTESTER_VERIFY(end >= 0); |
964 | MODELTESTER_VERIFY(start <= end); |
965 | int itemCount = orientation == Qt::Vertical ? model->rowCount() : model->columnCount(); |
966 | MODELTESTER_VERIFY(start < itemCount); |
967 | MODELTESTER_VERIFY(end < itemCount); |
968 | } |
969 | |
970 | bool QAbstractItemModelTesterPrivate::verify(bool statement, |
971 | const char *statementStr, const char *description, |
972 | const char *file, int line) |
973 | { |
974 | static const char formatString[] = "FAIL! %s (%s) returned FALSE (%s:%d)" ; |
975 | |
976 | switch (failureReportingMode) { |
977 | case QAbstractItemModelTester::FailureReportingMode::QtTest: |
978 | return QTest::qVerify(statement, statementStr, description, file, line); |
979 | break; |
980 | |
981 | case QAbstractItemModelTester::FailureReportingMode::Warning: |
982 | if (!statement) |
983 | qCWarning(lcModelTest, formatString, statementStr, description, file, line); |
984 | break; |
985 | |
986 | case QAbstractItemModelTester::FailureReportingMode::Fatal: |
987 | if (!statement) |
988 | qFatal(msg: formatString, statementStr, description, file, line); |
989 | break; |
990 | } |
991 | |
992 | return statement; |
993 | } |
994 | |
995 | |
996 | template<typename T1, typename T2> |
997 | bool QAbstractItemModelTesterPrivate::compare(const T1 &t1, const T2 &t2, |
998 | const char *actual, const char *expected, |
999 | const char *file, int line) |
1000 | { |
1001 | const bool result = static_cast<bool>(t1 == t2); |
1002 | |
1003 | static const char formatString[] = "FAIL! Compared values are not the same:\n Actual (%s) %s\n Expected (%s) %s\n (%s:%d)" ; |
1004 | |
1005 | switch (failureReportingMode) { |
1006 | case QAbstractItemModelTester::FailureReportingMode::QtTest: |
1007 | return QTest::qCompare(t1, t2, actual, expected, file, line); |
1008 | break; |
1009 | |
1010 | case QAbstractItemModelTester::FailureReportingMode::Warning: |
1011 | if (!result) { |
1012 | auto t1string = QTest::toString(t1); |
1013 | auto t2string = QTest::toString(t2); |
1014 | qCWarning(lcModelTest, formatString, actual, t1string ? t1string : "(nullptr)" , |
1015 | expected, t2string ? t2string : "(nullptr)" , |
1016 | file, line); |
1017 | delete [] t1string; |
1018 | delete [] t2string; |
1019 | } |
1020 | break; |
1021 | |
1022 | case QAbstractItemModelTester::FailureReportingMode::Fatal: |
1023 | if (!result) { |
1024 | auto t1string = QTest::toString(t1); |
1025 | auto t2string = QTest::toString(t2); |
1026 | qFatal(formatString, actual, t1string ? t1string : "(nullptr)" , |
1027 | expected, t2string ? t2string : "(nullptr)" , |
1028 | file, line); |
1029 | delete [] t1string; |
1030 | delete [] t2string; |
1031 | } |
1032 | break; |
1033 | } |
1034 | |
1035 | return result; |
1036 | } |
1037 | |
1038 | |
1039 | QT_END_NAMESPACE |
1040 | |
1041 | #include "moc_qabstractitemmodeltester.cpp" |
1042 | |