Merge lp:~aacid/unity/sortfilterproxymodelqml_split into lp:unity/phablet

Proposed by Albert Astals Cid
Status: Merged
Approved by: Michael Zanetti
Approved revision: no longer in the source branch.
Merged at revision: 606
Proposed branch: lp:~aacid/unity/sortfilterproxymodelqml_split
Merge into: lp:unity/phablet
Diff against target: 1959 lines (+1496/-261)
13 files modified
Components/FilterGrid.qml (+1/-1)
debian/copyright (+31/-0)
plugins/Utils/CMakeLists.txt (+1/-0)
plugins/Utils/plugin.cpp (+2/-0)
plugins/Utils/qlimitproxymodelqml.cpp (+166/-0)
plugins/Utils/qlimitproxymodelqml.h (+60/-0)
plugins/Utils/qsortfilterproxymodelqml.cpp (+13/-34)
plugins/Utils/qsortfilterproxymodelqml.h (+2/-6)
tests/plugins/Utils/CMakeLists.txt (+2/-1)
tests/plugins/Utils/modeltest.cpp (+605/-0)
tests/plugins/Utils/modeltest.h (+101/-0)
tests/plugins/Utils/qlimitproxymodeltest.cpp (+472/-0)
tests/plugins/Utils/qsortfilterproxymodeltest.cpp (+40/-219)
To merge this branch: bzr merge lp:~aacid/unity/sortfilterproxymodelqml_split
Reviewer Review Type Date Requested Status
Michael Zanetti (community) Approve
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+159174@code.launchpad.net

Commit message

Split the filterproxymodel in two

Since the limit feature did collide a bit with the rest of the implementation (it even had a qFatal) if you tried to use both

The new QLimitProxyModel inherits from QIdentityProxyModel instead of QSortFilterProxyModel

Adapt the testscases

To post a comment you must log in.
Revision history for this message
Michael Zanetti (mzanetti) wrote :

nitpick alert:
66 +// self
67 +#include "qlimitproxymodelqml.h"
68 +
69 +// Qt
70 +#include <QDebug>

// self & ""
// Qt & <Q*>
seems redundant to me...

157 + m_limit = limit;
158 + Q_EMIT limitChanged();
159 +
160 + if (inserting) {
161 + endInsertRows();
162 + } else if (removing) {
163 + endRemoveRows();
164 + }

I would try to avoid emitting signals in between begin*Rows() and end*Rows(). The reason is that someone using this model might connect to your signal and reading data from this model in between the transition.
This might not be harmful as long as this model is only used with standard Qt Views, but with custom view implementations this could be a potential pitfall.

196 +QLimitProxyModelQML::sourceRowsInserted(const QModelIndex & /*parent*/, int /*start*/, int /*end*/)

I'd prefer Q_UNUSED() (easier to get rid of in case you'll need it at some point), however, not a hard requirement from my side.

119 + if (parent.isValid()) // We are not a tree
120 + return 0;

One of the very rare places (imho) where even a Q_ASSERT() might make sense?

242 + Q_PROPERTY(QAbstractItemModel* model READ sourceModel WRITE setModel)

Please add NOTIFY signal or make it CONSTANT. Otherwise we'll have those nasty QML warnings "depends on non-notifyable property". As we have WRITE, I'd prefer the NOTIFY signal.

590 +// qDebug() << "hi";
Hi you too :) There's also lots of other commented stuff in the tests...

review: Needs Fixing
Revision history for this message
Albert Astals Cid (aacid) wrote :

The nitpicks are about keeping style consistent with the brother class, i can agree to them, but i'd prefer we discuss & change in a future MR

Moved the countChanged signal to after the rows inserted/removed signals

Made the model notifiable

The qDebugs are part of the original modeltest code in Qt, i'd prefer not to change it.

We can't assert there otheriwse modeltest will assert

I prefer commenting out than Q_UNUSED since most times when i start using the variable again, i forget to remove teh Q_UNUSED and then look like a silly person :D

Revision history for this message
Michael Zanetti (mzanetti) wrote :

looks good now. Lets wait for CI to approve too before top-approving

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Michael Zanetti (mzanetti) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Components/FilterGrid.qml'
2--- Components/FilterGrid.qml 2013-03-18 15:39:21 +0000
3+++ Components/FilterGrid.qml 2013-04-17 14:04:25 +0000
4@@ -83,7 +83,7 @@
5 delegateHeight: units.gu(9.5)
6 verticalSpacing: units.gu(2)
7
8- model: SortFilterProxyModel {
9+ model: LimitProxyModel {
10 model: root.model
11 limit: (filter) ? collapsedRowCount * iconTileGrid.columns : -1
12 }
13
14=== modified file 'debian/copyright'
15--- debian/copyright 2013-02-07 12:18:24 +0000
16+++ debian/copyright 2013-04-17 14:04:25 +0000
17@@ -19,3 +19,34 @@
18 .
19 On Debian systems, the full text of the GNU General Public License
20 version 3 can be found in the file /usr/share/common-licenses/GPL-3.
21+
22+Files: tests/unittests/plugins/Utils/modeltest.*
23+Copyright: 2013 Digia Plc and/or its subsidiary(-ies).
24+License:
25+ Commercial License Usage
26+ Licensees holding valid commercial Qt licenses may use this file in
27+ accordance with the commercial license agreement provided with the
28+ Software or, alternatively, in accordance with the terms contained in
29+ a written agreement between you and Digia. For licensing terms and
30+ conditions see http://qt.digia.com/licensing. For further information
31+ use the contact form at http://qt.digia.com/contact-us.
32+
33+ GNU Lesser General Public License Usage
34+ Alternatively, this file may be used under the terms of the GNU Lesser
35+ General Public License version 2.1 as published by the Free Software
36+ Foundation and appearing in the file LICENSE.LGPL included in the
37+ packaging of this file. Please review the following information to
38+ ensure the GNU Lesser General Public License version 2.1 requirements
39+ will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
40+
41+ In addition, as a special exception, Digia gives you certain additional
42+ rights. These rights are described in the Digia Qt LGPL Exception
43+ version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
44+
45+ GNU General Public License Usage
46+ Alternatively, this file may be used under the terms of the GNU
47+ General Public License version 3.0 as published by the Free Software
48+ Foundation and appearing in the file LICENSE.GPL included in the
49+ packaging of this file. Please review the following information to
50+ ensure the GNU General Public License version 3.0 requirements will be
51+ met: http://www.gnu.org/copyleft/gpl.html.
52
53=== modified file 'plugins/Utils/CMakeLists.txt'
54--- plugins/Utils/CMakeLists.txt 2013-04-12 11:36:33 +0000
55+++ plugins/Utils/CMakeLists.txt 2013-04-17 14:04:25 +0000
56@@ -5,6 +5,7 @@
57 )
58
59 set(QMLPLUGIN_SRC
60+ qlimitproxymodelqml.cpp
61 qsortfilterproxymodelqml.cpp
62 ubuntuwindow.cpp
63 plugin.cpp
64
65=== modified file 'plugins/Utils/plugin.cpp'
66--- plugins/Utils/plugin.cpp 2013-04-09 14:59:45 +0000
67+++ plugins/Utils/plugin.cpp 2013-04-17 14:04:25 +0000
68@@ -24,6 +24,7 @@
69 #include "plugin.h"
70
71 // local
72+#include "qlimitproxymodelqml.h"
73 #include "qsortfilterproxymodelqml.h"
74 #include "ubuntuwindow.h"
75
76@@ -31,6 +32,7 @@
77 {
78 Q_ASSERT(uri == QLatin1String("Utils"));
79 qmlRegisterType<QAbstractItemModel>();
80+ qmlRegisterType<QLimitProxyModelQML>(uri, 0, 1, "LimitProxyModel");
81 qmlRegisterType<QSortFilterProxyModelQML>(uri, 0, 1, "SortFilterProxyModel");
82 qmlRegisterExtendedType<QQuickWindow, UbuntuWindow>(uri, 0, 1, "Window");
83 }
84
85=== added file 'plugins/Utils/qlimitproxymodelqml.cpp'
86--- plugins/Utils/qlimitproxymodelqml.cpp 1970-01-01 00:00:00 +0000
87+++ plugins/Utils/qlimitproxymodelqml.cpp 2013-04-17 14:04:25 +0000
88@@ -0,0 +1,166 @@
89+/*
90+ * Copyright (C) 2012, 2013 Canonical, Ltd.
91+ *
92+ * This program is free software; you can redistribute it and/or modify
93+ * it under the terms of the GNU General Public License as published by
94+ * the Free Software Foundation; version 3.
95+ *
96+ * This program is distributed in the hope that it will be useful,
97+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
98+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
99+ * GNU General Public License for more details.
100+ *
101+ * You should have received a copy of the GNU General Public License
102+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
103+ */
104+
105+// self
106+#include "qlimitproxymodelqml.h"
107+
108+// Qt
109+#include <QDebug>
110+
111+QLimitProxyModelQML::QLimitProxyModelQML(QObject *parent)
112+ : QIdentityProxyModel(parent)
113+ , m_limit(-1)
114+ , m_sourceInserting(false)
115+ , m_sourceRemoving(false)
116+{
117+ connect(this, SIGNAL(modelReset()), SIGNAL(countChanged()));
118+ connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), SIGNAL(countChanged()));
119+ connect(this, SIGNAL(rowsRemoved(QModelIndex,int,int)), SIGNAL(countChanged()));
120+}
121+
122+QHash<int, QByteArray> QLimitProxyModelQML::roleNames() const
123+{
124+ return sourceModel() ? sourceModel()->roleNames() : QHash<int, QByteArray>();
125+}
126+
127+void
128+QLimitProxyModelQML::setModel(QAbstractItemModel *itemModel)
129+{
130+ if (sourceModel() != NULL) {
131+ sourceModel()->disconnect(this);
132+ }
133+
134+ if (itemModel != sourceModel()) {
135+ setSourceModel(itemModel);
136+
137+ if (sourceModel() != NULL) {
138+ // Disconnect the QIdentityProxyModel handling for rows removed/added...
139+ disconnect(sourceModel(), SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), this, NULL);
140+ disconnect(sourceModel(), SIGNAL(rowsInserted(QModelIndex,int,int)), this, NULL);
141+ disconnect(sourceModel(), SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), this, NULL);
142+ disconnect(sourceModel(), SIGNAL(rowsRemoved(QModelIndex,int,int)), this, NULL);
143+
144+ // ... and use our own
145+ connect(sourceModel(), SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
146+ this, SLOT(sourceRowsAboutToBeInserted(QModelIndex,int,int)));
147+ connect(sourceModel(), SIGNAL(rowsInserted(QModelIndex,int,int)),
148+ this, SLOT(sourceRowsInserted(QModelIndex,int,int)));
149+ connect(sourceModel(), SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
150+ this, SLOT(sourceRowsAboutToBeRemoved(QModelIndex,int,int)));
151+ connect(sourceModel(), SIGNAL(rowsRemoved(QModelIndex,int,int)),
152+ this, SLOT(sourceRowsRemoved(QModelIndex,int,int)));
153+ }
154+ Q_EMIT modelChanged();
155+ }
156+}
157+
158+int
159+QLimitProxyModelQML::rowCount(const QModelIndex &parent) const
160+{
161+ if (parent.isValid()) // We are not a tree
162+ return 0;
163+
164+ const int unlimitedCount = QIdentityProxyModel::rowCount(parent);
165+ return m_limit < 0 ? unlimitedCount : qMin(m_limit, unlimitedCount);
166+}
167+
168+int
169+QLimitProxyModelQML::limit() const
170+{
171+ return m_limit;
172+}
173+
174+void
175+QLimitProxyModelQML::setLimit(int limit)
176+{
177+ if (limit != m_limit) {
178+ bool inserting = false;
179+ bool removing = false;
180+ const int oldCount = rowCount();
181+ const int unlimitedCount = QIdentityProxyModel::rowCount();
182+ if (m_limit < 0) {
183+ if (limit < oldCount) {
184+ removing = true;
185+ beginRemoveRows(QModelIndex(), limit, oldCount - 1);
186+ }
187+ } else if (limit < 0) {
188+ if (m_limit < unlimitedCount) {
189+ inserting = true;
190+ beginInsertRows(QModelIndex(), m_limit, unlimitedCount - 1);
191+ }
192+ } else {
193+ if (limit > m_limit && unlimitedCount > m_limit) {
194+ inserting = true;
195+ beginInsertRows(QModelIndex(), m_limit, qMin(limit, unlimitedCount) - 1);
196+ } else if (limit < m_limit && limit < oldCount) {
197+ removing = true;
198+ beginRemoveRows(QModelIndex(), limit, oldCount - 1);
199+ }
200+ }
201+
202+ m_limit = limit;
203+
204+ if (inserting) {
205+ endInsertRows();
206+ } else if (removing) {
207+ endRemoveRows();
208+ }
209+
210+ Q_EMIT limitChanged();
211+ }
212+}
213+
214+void
215+QLimitProxyModelQML::sourceRowsAboutToBeInserted(const QModelIndex &parent, int start, int end)
216+{
217+ if (m_limit < 0) {
218+ beginInsertRows(mapFromSource(parent), start, end);
219+ m_sourceInserting = true;
220+ } else if (start < m_limit) {
221+ beginInsertRows(mapFromSource(parent), start, qMin(m_limit - 1, end));
222+ m_sourceInserting = true;
223+ }
224+}
225+
226+void
227+QLimitProxyModelQML::sourceRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
228+{
229+ if (m_limit < 0) {
230+ beginRemoveRows(mapFromSource(parent), start, end);
231+ m_sourceRemoving = true;
232+ } else if (start < m_limit) {
233+ beginRemoveRows(mapFromSource(parent), start, qMin(m_limit - 1, end));
234+ m_sourceRemoving = true;
235+ }
236+}
237+
238+void
239+QLimitProxyModelQML::sourceRowsInserted(const QModelIndex & /*parent*/, int /*start*/, int /*end*/)
240+{
241+ if (m_sourceInserting) {
242+ endInsertRows();
243+ m_sourceInserting = false;
244+ }
245+}
246+
247+void
248+QLimitProxyModelQML::sourceRowsRemoved(const QModelIndex & /*parent*/, int /*start*/, int /*end*/)
249+{
250+ if (m_sourceRemoving) {
251+ endRemoveRows();
252+ m_sourceRemoving = false;
253+ }
254+}
255
256=== added file 'plugins/Utils/qlimitproxymodelqml.h'
257--- plugins/Utils/qlimitproxymodelqml.h 1970-01-01 00:00:00 +0000
258+++ plugins/Utils/qlimitproxymodelqml.h 2013-04-17 14:04:25 +0000
259@@ -0,0 +1,60 @@
260+/*
261+ * Copyright (C) 2012, 2013 Canonical, Ltd.
262+ *
263+ * This program is free software; you can redistribute it and/or modify
264+ * it under the terms of the GNU General Public License as published by
265+ * the Free Software Foundation; version 3.
266+ *
267+ * This program is distributed in the hope that it will be useful,
268+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
269+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
270+ * GNU General Public License for more details.
271+ *
272+ * You should have received a copy of the GNU General Public License
273+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
274+ */
275+
276+#ifndef QLIMITPROXYMODELQML_H
277+#define QLIMITPROXYMODELQML_H
278+
279+#include <QIdentityProxyModel>
280+
281+class QLimitProxyModelQML : public QIdentityProxyModel
282+{
283+ Q_OBJECT
284+
285+ Q_PROPERTY(QAbstractItemModel* model READ sourceModel WRITE setModel NOTIFY modelChanged)
286+ Q_PROPERTY(int limit READ limit WRITE setLimit NOTIFY limitChanged)
287+ Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
288+
289+public:
290+ explicit QLimitProxyModelQML(QObject *parent = 0);
291+
292+ /* getters */
293+ int limit() const;
294+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
295+ QHash<int, QByteArray> roleNames() const;
296+
297+ /* setters */
298+ void setModel(QAbstractItemModel *model);
299+ void setLimit(int limit);
300+
301+Q_SIGNALS:
302+ void limitChanged();
303+ void totalCountChanged();
304+ void countChanged();
305+ void modelChanged();
306+
307+private Q_SLOTS:
308+ void sourceRowsAboutToBeInserted(const QModelIndex &parent, int start, int end);
309+ void sourceRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end);
310+ void sourceRowsInserted(const QModelIndex &parent, int start, int end);
311+ void sourceRowsRemoved(const QModelIndex &parent, int start, int end);
312+
313+private:
314+ int m_limit;
315+ bool m_sourceInserting;
316+ bool m_sourceRemoving;
317+};
318+
319+#endif // QLIMITPROXYMODELQML_H
320
321=== modified file 'plugins/Utils/qsortfilterproxymodelqml.cpp'
322--- plugins/Utils/qsortfilterproxymodelqml.cpp 2013-04-15 14:29:04 +0000
323+++ plugins/Utils/qsortfilterproxymodelqml.cpp 2013-04-17 14:04:25 +0000
324@@ -22,7 +22,6 @@
325
326 QSortFilterProxyModelQML::QSortFilterProxyModelQML(QObject *parent)
327 : QSortFilterProxyModel(parent)
328- , m_limit(-1)
329 , m_invertMatch(false)
330 {
331 connect(this, SIGNAL(modelReset()), SIGNAL(countChanged()));
332@@ -42,17 +41,20 @@
333 return;
334 }
335
336- if (sourceModel() != NULL) {
337- sourceModel()->disconnect(this);
338+ if (itemModel != sourceModel()) {
339+ if (sourceModel() != NULL) {
340+ sourceModel()->disconnect(this);
341+ }
342+
343+ setSourceModel(itemModel);
344+
345+ connect(itemModel, SIGNAL(modelReset()), SIGNAL(totalCountChanged()));
346+ connect(itemModel, SIGNAL(rowsInserted(QModelIndex,int,int)), SIGNAL(totalCountChanged()));
347+ connect(itemModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), SIGNAL(totalCountChanged()));
348+ Q_EMIT totalCountChanged();
349+ Q_EMIT countChanged();
350+ Q_EMIT modelChanged();
351 }
352-
353- setSourceModel(itemModel);
354-
355- connect(itemModel, SIGNAL(modelReset()), SIGNAL(totalCountChanged()));
356- connect(itemModel, SIGNAL(rowsInserted(QModelIndex,int,int)), SIGNAL(totalCountChanged()));
357- connect(itemModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), SIGNAL(totalCountChanged()));
358- Q_EMIT totalCountChanged();
359- Q_EMIT countChanged();
360 }
361
362 QVariantMap
363@@ -88,26 +90,6 @@
364 return rowCount();
365 }
366
367-int
368-QSortFilterProxyModelQML::limit() const
369-{
370- return m_limit;
371-}
372-
373-void
374-QSortFilterProxyModelQML::setLimit(int limit)
375-{
376- if (limit != -1 && !filterRegExp().isEmpty()) {
377- qFatal("QSortFilterProxyModel: filterRegExp and limit are both set which is not supported");
378- }
379-
380- if (limit != m_limit) {
381- m_limit = limit;
382- invalidateFilter();
383- Q_EMIT limitChanged();
384- }
385-}
386-
387 bool
388 QSortFilterProxyModelQML::invertMatch() const
389 {
390@@ -127,9 +109,6 @@
391 QSortFilterProxyModelQML::filterAcceptsRow(int sourceRow,
392 const QModelIndex &sourceParent) const
393 {
394- if (m_limit != -1 && sourceRow >= m_limit) {
395- return false;
396- }
397 // If there's no regexp set, always accept all rows indepenently of the invertMatch setting
398 if (filterRegExp().isEmpty()) {
399 return true;
400
401=== modified file 'plugins/Utils/qsortfilterproxymodelqml.h'
402--- plugins/Utils/qsortfilterproxymodelqml.h 2013-04-12 14:43:25 +0000
403+++ plugins/Utils/qsortfilterproxymodelqml.h 2013-04-17 14:04:25 +0000
404@@ -23,8 +23,7 @@
405 {
406 Q_OBJECT
407
408- Q_PROPERTY(QAbstractItemModel* model READ sourceModel WRITE setModel)
409- Q_PROPERTY(int limit READ limit WRITE setLimit NOTIFY limitChanged)
410+ Q_PROPERTY(QAbstractItemModel* model READ sourceModel WRITE setModel NOTIFY modelChanged)
411 Q_PROPERTY(int totalCount READ totalCount NOTIFY totalCountChanged)
412 Q_PROPERTY(int count READ count NOTIFY countChanged)
413 Q_PROPERTY(bool invertMatch READ invertMatch WRITE setInvertMatch NOTIFY invertMatchChanged)
414@@ -38,24 +37,21 @@
415 virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
416
417 /* getters */
418- int limit() const;
419 int totalCount() const;
420 bool invertMatch() const;
421 QHash<int, QByteArray> roleNames() const;
422
423 /* setters */
424 void setModel(QAbstractItemModel *model);
425- void setLimit(int limit);
426 void setInvertMatch(bool invertMatch);
427
428 Q_SIGNALS:
429- void limitChanged();
430 void totalCountChanged();
431 void countChanged();
432 void invertMatchChanged(bool);
433+ void modelChanged();
434
435 private:
436- int m_limit;
437 bool m_invertMatch;
438 };
439
440
441=== removed directory 'plugins/Utils/tests'
442=== removed directory 'plugins/Utils/tests/CMakeFiles'
443=== modified file 'tests/plugins/Utils/CMakeLists.txt'
444--- tests/plugins/Utils/CMakeLists.txt 2013-04-16 14:22:23 +0000
445+++ tests/plugins/Utils/CMakeLists.txt 2013-04-17 14:04:25 +0000
446@@ -12,7 +12,7 @@
447 -o ${CMAKE_BINARY_DIR}/${_test}.xml,xunitxml
448 -o -,txt
449 )
450- add_executable(${_test} ${_test}.cpp)
451+ add_executable(${_test} ${_test}.cpp modeltest.cpp)
452 qt5_use_modules(${_test} Test Core Qml)
453 target_link_libraries(${_test}
454 Utils-qml
455@@ -22,5 +22,6 @@
456 endmacro(run_tests)
457
458 run_tests(
459+ qlimitproxymodeltest
460 qsortfilterproxymodeltest
461 )
462
463=== added file 'tests/plugins/Utils/modeltest.cpp'
464--- tests/plugins/Utils/modeltest.cpp 1970-01-01 00:00:00 +0000
465+++ tests/plugins/Utils/modeltest.cpp 2013-04-17 14:04:25 +0000
466@@ -0,0 +1,605 @@
467+/****************************************************************************
468+** The full license is below, this is here to make licensecheck happy
469+**
470+** This program is free software; you can redistribute it and/or modify
471+** it under the terms of the GNU Lesser General Public License as published by
472+** the Free Software Foundation; version 2.1.
473+**
474+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
475+** Contact: http://www.qt-project.org/legal
476+**
477+** This file is part of the test suite of the Qt Toolkit.
478+**
479+** $QT_BEGIN_LICENSE:LGPL$
480+** Commercial License Usage
481+** Licensees holding valid commercial Qt licenses may use this file in
482+** accordance with the commercial license agreement provided with the
483+** Software or, alternatively, in accordance with the terms contained in
484+** a written agreement between you and Digia. For licensing terms and
485+** conditions see http://qt.digia.com/licensing. For further information
486+** use the contact form at http://qt.digia.com/contact-us.
487+**
488+** GNU Lesser General Public License Usage
489+** Alternatively, this file may be used under the terms of the GNU Lesser
490+** General Public License version 2.1 as published by the Free Software
491+** Foundation and appearing in the file LICENSE.LGPL included in the
492+** packaging of this file. Please review the following information to
493+** ensure the GNU Lesser General Public License version 2.1 requirements
494+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
495+**
496+** In addition, as a special exception, Digia gives you certain additional
497+** rights. These rights are described in the Digia Qt LGPL Exception
498+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
499+**
500+** GNU General Public License Usage
501+** Alternatively, this file may be used under the terms of the GNU
502+** General Public License version 3.0 as published by the Free Software
503+** Foundation and appearing in the file LICENSE.GPL included in the
504+** packaging of this file. Please review the following information to
505+** ensure the GNU General Public License version 3.0 requirements will be
506+** met: http://www.gnu.org/copyleft/gpl.html.
507+**
508+**
509+** $QT_END_LICENSE$
510+**
511+****************************************************************************/
512+
513+
514+#include <QtGui/QtGui>
515+
516+#include "modeltest.h"
517+
518+#include <QtTest/QtTest>
519+
520+/*!
521+ Connect to all of the models signals. Whenever anything happens recheck everything.
522+*/
523+ModelTest::ModelTest ( QAbstractItemModel *_model, QObject *parent ) : QObject ( parent ), model ( _model ), fetchingMore ( false )
524+{
525+ if (!model)
526+ qFatal("%s: model must not be null", Q_FUNC_INFO);
527+
528+ connect(model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)),
529+ this, SLOT(runAllTests()) );
530+ connect(model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
531+ this, SLOT(runAllTests()) );
532+ connect(model, SIGNAL(columnsInserted(QModelIndex,int,int)),
533+ this, SLOT(runAllTests()) );
534+ connect(model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
535+ this, SLOT(runAllTests()) );
536+ connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
537+ this, SLOT(runAllTests()) );
538+ connect(model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)),
539+ this, SLOT(runAllTests()) );
540+ connect(model, SIGNAL(layoutAboutToBeChanged()), this, SLOT(runAllTests()) );
541+ connect(model, SIGNAL(layoutChanged()), this, SLOT(runAllTests()) );
542+ connect(model, SIGNAL(modelReset()), this, SLOT(runAllTests()) );
543+ connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
544+ this, SLOT(runAllTests()) );
545+ connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
546+ this, SLOT(runAllTests()) );
547+ connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)),
548+ this, SLOT(runAllTests()) );
549+ connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
550+ this, SLOT(runAllTests()) );
551+
552+ // Special checks for changes
553+ connect(model, SIGNAL(layoutAboutToBeChanged()),
554+ this, SLOT(layoutAboutToBeChanged()) );
555+ connect(model, SIGNAL(layoutChanged()),
556+ this, SLOT(layoutChanged()) );
557+
558+ connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
559+ this, SLOT(rowsAboutToBeInserted(QModelIndex,int,int)) );
560+ connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
561+ this, SLOT(rowsAboutToBeRemoved(QModelIndex,int,int)) );
562+ connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)),
563+ this, SLOT(rowsInserted(QModelIndex,int,int)) );
564+ connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
565+ this, SLOT(rowsRemoved(QModelIndex,int,int)) );
566+ connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
567+ this, SLOT(dataChanged(QModelIndex,QModelIndex)) );
568+ connect(model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)),
569+ this, SLOT(headerDataChanged(Qt::Orientation,int,int)) );
570+
571+ runAllTests();
572+}
573+
574+void ModelTest::runAllTests()
575+{
576+ if ( fetchingMore )
577+ return;
578+ nonDestructiveBasicTest();
579+ rowCount();
580+ columnCount();
581+ hasIndex();
582+ index();
583+ parent();
584+ data();
585+}
586+
587+/*!
588+ nonDestructiveBasicTest tries to call a number of the basic functions (not all)
589+ to make sure the model doesn't outright segfault, testing the functions that makes sense.
590+*/
591+void ModelTest::nonDestructiveBasicTest()
592+{
593+ QVERIFY( model->buddy ( QModelIndex() ) == QModelIndex() );
594+ model->canFetchMore ( QModelIndex() );
595+ QVERIFY( model->columnCount ( QModelIndex() ) >= 0 );
596+ QVERIFY( model->data ( QModelIndex() ) == QVariant() );
597+ fetchingMore = true;
598+ model->fetchMore ( QModelIndex() );
599+ fetchingMore = false;
600+ Qt::ItemFlags flags = model->flags ( QModelIndex() );
601+ QVERIFY( flags == Qt::ItemIsDropEnabled || flags == 0 );
602+ model->hasChildren ( QModelIndex() );
603+ model->hasIndex ( 0, 0 );
604+ model->headerData ( 0, Qt::Horizontal );
605+ model->index ( 0, 0 );
606+ model->itemData ( QModelIndex() );
607+ QVariant cache;
608+ model->match ( QModelIndex(), -1, cache );
609+ model->mimeTypes();
610+ QVERIFY( model->parent ( QModelIndex() ) == QModelIndex() );
611+ QVERIFY( model->rowCount() >= 0 );
612+ QVariant variant;
613+ model->setData ( QModelIndex(), variant, -1 );
614+ model->setHeaderData ( -1, Qt::Horizontal, QVariant() );
615+ model->setHeaderData ( 999999, Qt::Horizontal, QVariant() );
616+ QMap<int, QVariant> roles;
617+ model->sibling ( 0, 0, QModelIndex() );
618+ model->span ( QModelIndex() );
619+ model->supportedDropActions();
620+}
621+
622+/*!
623+ Tests model's implementation of QAbstractItemModel::rowCount() and hasChildren()
624+
625+ Models that are dynamically populated are not as fully tested here.
626+ */
627+void ModelTest::rowCount()
628+{
629+// qDebug() << "rc";
630+ // check top row
631+ QModelIndex topIndex = model->index ( 0, 0, QModelIndex() );
632+ int rows = model->rowCount ( topIndex );
633+ QVERIFY( rows >= 0 );
634+ if ( rows > 0 )
635+ QVERIFY( model->hasChildren ( topIndex ) );
636+
637+ QModelIndex secondLevelIndex = model->index ( 0, 0, topIndex );
638+ if ( secondLevelIndex.isValid() ) { // not the top level
639+ // check a row count where parent is valid
640+ rows = model->rowCount ( secondLevelIndex );
641+ QVERIFY( rows >= 0 );
642+ if ( rows > 0 )
643+ QVERIFY( model->hasChildren ( secondLevelIndex ) );
644+ }
645+
646+ // The models rowCount() is tested more extensively in checkChildren(),
647+ // but this catches the big mistakes
648+}
649+
650+/*!
651+ Tests model's implementation of QAbstractItemModel::columnCount() and hasChildren()
652+ */
653+void ModelTest::columnCount()
654+{
655+ // check top row
656+ QModelIndex topIndex = model->index ( 0, 0, QModelIndex() );
657+ QVERIFY( model->columnCount ( topIndex ) >= 0 );
658+
659+ // check a column count where parent is valid
660+ QModelIndex childIndex = model->index ( 0, 0, topIndex );
661+ if ( childIndex.isValid() )
662+ QVERIFY( model->columnCount ( childIndex ) >= 0 );
663+
664+ // columnCount() is tested more extensively in checkChildren(),
665+ // but this catches the big mistakes
666+}
667+
668+/*!
669+ Tests model's implementation of QAbstractItemModel::hasIndex()
670+ */
671+void ModelTest::hasIndex()
672+{
673+// qDebug() << "hi";
674+ // Make sure that invalid values returns an invalid index
675+ QVERIFY( !model->hasIndex ( -2, -2 ) );
676+ QVERIFY( !model->hasIndex ( -2, 0 ) );
677+ QVERIFY( !model->hasIndex ( 0, -2 ) );
678+
679+ int rows = model->rowCount();
680+ int columns = model->columnCount();
681+
682+ // check out of bounds
683+ QVERIFY( !model->hasIndex ( rows, columns ) );
684+ QVERIFY( !model->hasIndex ( rows + 1, columns + 1 ) );
685+
686+ if ( rows > 0 )
687+ QVERIFY( model->hasIndex ( 0, 0 ) );
688+
689+ // hasIndex() is tested more extensively in checkChildren(),
690+ // but this catches the big mistakes
691+}
692+
693+/*!
694+ Tests model's implementation of QAbstractItemModel::index()
695+ */
696+void ModelTest::index()
697+{
698+// qDebug() << "i";
699+ // Make sure that invalid values returns an invalid index
700+ QVERIFY( model->index ( -2, -2 ) == QModelIndex() );
701+ QVERIFY( model->index ( -2, 0 ) == QModelIndex() );
702+ QVERIFY( model->index ( 0, -2 ) == QModelIndex() );
703+
704+ int rows = model->rowCount();
705+ int columns = model->columnCount();
706+
707+ if ( rows == 0 )
708+ return;
709+
710+ // Catch off by one errors
711+ QVERIFY( model->index ( rows, columns ) == QModelIndex() );
712+ QVERIFY( model->index ( 0, 0 ).isValid() );
713+
714+ // Make sure that the same index is *always* returned
715+ QModelIndex a = model->index ( 0, 0 );
716+ QModelIndex b = model->index ( 0, 0 );
717+ QVERIFY( a == b );
718+
719+ // index() is tested more extensively in checkChildren(),
720+ // but this catches the big mistakes
721+}
722+
723+/*!
724+ Tests model's implementation of QAbstractItemModel::parent()
725+ */
726+void ModelTest::parent()
727+{
728+// qDebug() << "p";
729+ // Make sure the model won't crash and will return an invalid QModelIndex
730+ // when asked for the parent of an invalid index.
731+ QVERIFY( model->parent ( QModelIndex() ) == QModelIndex() );
732+
733+ if ( model->rowCount() == 0 )
734+ return;
735+
736+ // Column 0 | Column 1 |
737+ // QModelIndex() | |
738+ // \- topIndex | topIndex1 |
739+ // \- childIndex | childIndex1 |
740+
741+ // Common error test #1, make sure that a top level index has a parent
742+ // that is a invalid QModelIndex.
743+ QModelIndex topIndex = model->index ( 0, 0, QModelIndex() );
744+ QVERIFY( model->parent ( topIndex ) == QModelIndex() );
745+
746+ // Common error test #2, make sure that a second level index has a parent
747+ // that is the first level index.
748+ if ( model->rowCount ( topIndex ) > 0 ) {
749+ QModelIndex childIndex = model->index ( 0, 0, topIndex );
750+ QVERIFY( model->parent ( childIndex ) == topIndex );
751+ }
752+
753+ // Common error test #3, the second column should NOT have the same children
754+ // as the first column in a row.
755+ // Usually the second column shouldn't have children.
756+ QModelIndex topIndex1 = model->index ( 0, 1, QModelIndex() );
757+ if ( model->rowCount ( topIndex1 ) > 0 ) {
758+ QModelIndex childIndex = model->index ( 0, 0, topIndex );
759+ QModelIndex childIndex1 = model->index ( 0, 0, topIndex1 );
760+ QVERIFY( childIndex != childIndex1 );
761+ }
762+
763+ // Full test, walk n levels deep through the model making sure that all
764+ // parent's children correctly specify their parent.
765+ checkChildren ( QModelIndex() );
766+}
767+
768+/*!
769+ Called from the parent() test.
770+
771+ A model that returns an index of parent X should also return X when asking
772+ for the parent of the index.
773+
774+ This recursive function does pretty extensive testing on the whole model in an
775+ effort to catch edge cases.
776+
777+ This function assumes that rowCount(), columnCount() and index() already work.
778+ If they have a bug it will point it out, but the above tests should have already
779+ found the basic bugs because it is easier to figure out the problem in
780+ those tests then this one.
781+ */
782+void ModelTest::checkChildren ( const QModelIndex &parent, int currentDepth )
783+{
784+ // First just try walking back up the tree.
785+ QModelIndex p = parent;
786+ while ( p.isValid() )
787+ p = p.parent();
788+
789+ // For models that are dynamically populated
790+ if ( model->canFetchMore ( parent ) ) {
791+ fetchingMore = true;
792+ model->fetchMore ( parent );
793+ fetchingMore = false;
794+ }
795+
796+ int rows = model->rowCount ( parent );
797+ int columns = model->columnCount ( parent );
798+
799+ if ( rows > 0 )
800+ QVERIFY( model->hasChildren ( parent ) );
801+
802+ // Some further testing against rows(), columns(), and hasChildren()
803+ QVERIFY( rows >= 0 );
804+ QVERIFY( columns >= 0 );
805+ if ( rows > 0 )
806+ QVERIFY( model->hasChildren ( parent ) );
807+
808+ //qDebug() << "parent:" << model->data(parent).toString() << "rows:" << rows
809+ // << "columns:" << columns << "parent column:" << parent.column();
810+
811+ const QModelIndex topLeftChild = model->index( 0, 0, parent );
812+
813+ QVERIFY( !model->hasIndex ( rows + 1, 0, parent ) );
814+ for ( int r = 0; r < rows; ++r ) {
815+ if ( model->canFetchMore ( parent ) ) {
816+ fetchingMore = true;
817+ model->fetchMore ( parent );
818+ fetchingMore = false;
819+ }
820+ QVERIFY( !model->hasIndex ( r, columns + 1, parent ) );
821+ for ( int c = 0; c < columns; ++c ) {
822+ QVERIFY( model->hasIndex ( r, c, parent ) );
823+ QModelIndex index = model->index ( r, c, parent );
824+ // rowCount() and columnCount() said that it existed...
825+ QVERIFY( index.isValid() );
826+
827+ // index() should always return the same index when called twice in a row
828+ QModelIndex modifiedIndex = model->index ( r, c, parent );
829+ QVERIFY( index == modifiedIndex );
830+
831+ // Make sure we get the same index if we request it twice in a row
832+ QModelIndex a = model->index ( r, c, parent );
833+ QModelIndex b = model->index ( r, c, parent );
834+ QVERIFY( a == b );
835+
836+ {
837+ const QModelIndex sibling = model->sibling( r, c, topLeftChild );
838+ QVERIFY( index == sibling );
839+ }
840+ {
841+ const QModelIndex sibling = topLeftChild.sibling( r, c );
842+ QVERIFY( index == sibling );
843+ }
844+
845+ // Some basic checking on the index that is returned
846+ QVERIFY( index.model() == model );
847+ QCOMPARE( index.row(), r );
848+ QCOMPARE( index.column(), c );
849+ // While you can technically return a QVariant usually this is a sign
850+ // of a bug in data(). Disable if this really is ok in your model.
851+// QVERIFY( model->data ( index, Qt::DisplayRole ).isValid() );
852+
853+ // If the next test fails here is some somewhat useful debug you play with.
854+
855+ if (model->parent(index) != parent) {
856+ qDebug() << r << c << currentDepth << model->data(index).toString()
857+ << model->data(parent).toString();
858+ qDebug() << index << parent << model->parent(index);
859+// And a view that you can even use to show the model.
860+// QTreeView view;
861+// view.setModel(model);
862+// view.show();
863+ }
864+
865+ // Check that we can get back our real parent.
866+ QCOMPARE( model->parent ( index ), parent );
867+
868+ // recursively go down the children
869+ if ( model->hasChildren ( index ) && currentDepth < 10 ) {
870+ //qDebug() << r << c << "has children" << model->rowCount(index);
871+ checkChildren ( index, ++currentDepth );
872+ }/* else { if (currentDepth >= 10) qDebug() << "checked 10 deep"; };*/
873+
874+ // make sure that after testing the children that the index doesn't change.
875+ QModelIndex newerIndex = model->index ( r, c, parent );
876+ QVERIFY( index == newerIndex );
877+ }
878+ }
879+}
880+
881+/*!
882+ Tests model's implementation of QAbstractItemModel::data()
883+ */
884+void ModelTest::data()
885+{
886+ // Invalid index should return an invalid qvariant
887+ QVERIFY( !model->data ( QModelIndex() ).isValid() );
888+
889+ if ( model->rowCount() == 0 )
890+ return;
891+
892+ // A valid index should have a valid QVariant data
893+ QVERIFY( model->index ( 0, 0 ).isValid() );
894+
895+ // shouldn't be able to set data on an invalid index
896+ QVERIFY( !model->setData ( QModelIndex(), QLatin1String ( "foo" ), Qt::DisplayRole ) );
897+
898+ // General Purpose roles that should return a QString
899+ QVariant variant = model->data ( model->index ( 0, 0 ), Qt::ToolTipRole );
900+ if ( variant.isValid() ) {
901+ QVERIFY( variant.canConvert<QString>() );
902+ }
903+ variant = model->data ( model->index ( 0, 0 ), Qt::StatusTipRole );
904+ if ( variant.isValid() ) {
905+ QVERIFY( variant.canConvert<QString>() );
906+ }
907+ variant = model->data ( model->index ( 0, 0 ), Qt::WhatsThisRole );
908+ if ( variant.isValid() ) {
909+ QVERIFY( variant.canConvert<QString>() );
910+ }
911+
912+ // General Purpose roles that should return a QSize
913+ variant = model->data ( model->index ( 0, 0 ), Qt::SizeHintRole );
914+ if ( variant.isValid() ) {
915+ QVERIFY( variant.canConvert<QSize>() );
916+ }
917+
918+ // General Purpose roles that should return a QFont
919+ QVariant fontVariant = model->data ( model->index ( 0, 0 ), Qt::FontRole );
920+ if ( fontVariant.isValid() ) {
921+ QVERIFY( fontVariant.canConvert<QFont>() );
922+ }
923+
924+ // Check that the alignment is one we know about
925+ QVariant textAlignmentVariant = model->data ( model->index ( 0, 0 ), Qt::TextAlignmentRole );
926+ if ( textAlignmentVariant.isValid() ) {
927+ int alignment = textAlignmentVariant.toInt();
928+ QCOMPARE( alignment, ( alignment & ( Qt::AlignHorizontal_Mask | Qt::AlignVertical_Mask ) ) );
929+ }
930+
931+ // General Purpose roles that should return a QColor
932+ QVariant colorVariant = model->data ( model->index ( 0, 0 ), Qt::BackgroundColorRole );
933+ if ( colorVariant.isValid() ) {
934+ QVERIFY( colorVariant.canConvert<QColor>() );
935+ }
936+
937+ colorVariant = model->data ( model->index ( 0, 0 ), Qt::TextColorRole );
938+ if ( colorVariant.isValid() ) {
939+ QVERIFY( colorVariant.canConvert<QColor>() );
940+ }
941+
942+ // Check that the "check state" is one we know about.
943+ QVariant checkStateVariant = model->data ( model->index ( 0, 0 ), Qt::CheckStateRole );
944+ if ( checkStateVariant.isValid() ) {
945+ int state = checkStateVariant.toInt();
946+ QVERIFY( state == Qt::Unchecked ||
947+ state == Qt::PartiallyChecked ||
948+ state == Qt::Checked );
949+ }
950+}
951+
952+/*!
953+ Store what is about to be inserted to make sure it actually happens
954+
955+ \sa rowsInserted()
956+ */
957+void ModelTest::rowsAboutToBeInserted ( const QModelIndex &parent, int start, int /* end */)
958+{
959+// Q_UNUSED(end);
960+// qDebug() << "rowsAboutToBeInserted" << "start=" << start << "end=" << end << "parent=" << model->data ( parent ).toString()
961+// << "current count of parent=" << model->rowCount ( parent ); // << "display of last=" << model->data( model->index(start-1, 0, parent) );
962+// qDebug() << model->index(start-1, 0, parent) << model->data( model->index(start-1, 0, parent) );
963+ Changing c;
964+ c.parent = parent;
965+ c.oldSize = model->rowCount ( parent );
966+ c.last = model->data ( model->index ( start - 1, 0, parent ) );
967+ c.next = model->data ( model->index ( start, 0, parent ) );
968+ insert.push ( c );
969+}
970+
971+/*!
972+ Confirm that what was said was going to happen actually did
973+
974+ \sa rowsAboutToBeInserted()
975+ */
976+void ModelTest::rowsInserted ( const QModelIndex & parent, int start, int end )
977+{
978+ Changing c = insert.pop();
979+ QVERIFY( c.parent == parent );
980+// qDebug() << "rowsInserted" << "start=" << start << "end=" << end << "oldsize=" << c.oldSize
981+// << "parent=" << model->data ( parent ).toString() << "current rowcount of parent=" << model->rowCount ( parent );
982+
983+// for (int ii=start; ii <= end; ii++)
984+// {
985+// qDebug() << "itemWasInserted:" << ii << model->data ( model->index ( ii, 0, parent ));
986+// }
987+// qDebug();
988+
989+ QVERIFY( c.oldSize + ( end - start + 1 ) == model->rowCount ( parent ) );
990+ QVERIFY( c.last == model->data ( model->index ( start - 1, 0, c.parent ) ) );
991+
992+ if (c.next != model->data(model->index(end + 1, 0, c.parent))) {
993+ qDebug() << start << end;
994+ for (int i=0; i < model->rowCount(); ++i)
995+ qDebug() << model->index(i, 0).data().toString();
996+ qDebug() << c.next << model->data(model->index(end + 1, 0, c.parent));
997+ }
998+
999+ QVERIFY( c.next == model->data ( model->index ( end + 1, 0, c.parent ) ) );
1000+}
1001+
1002+void ModelTest::layoutAboutToBeChanged()
1003+{
1004+ for ( int i = 0; i < qBound ( 0, model->rowCount(), 100 ); ++i )
1005+ changing.append ( QPersistentModelIndex ( model->index ( i, 0 ) ) );
1006+}
1007+
1008+void ModelTest::layoutChanged()
1009+{
1010+ for ( int i = 0; i < changing.count(); ++i ) {
1011+ QPersistentModelIndex p = changing[i];
1012+ QVERIFY( p == model->index ( p.row(), p.column(), p.parent() ) );
1013+ }
1014+ changing.clear();
1015+}
1016+
1017+/*!
1018+ Store what is about to be inserted to make sure it actually happens
1019+
1020+ \sa rowsRemoved()
1021+ */
1022+void ModelTest::rowsAboutToBeRemoved ( const QModelIndex &parent, int start, int end )
1023+{
1024+qDebug() << "ratbr" << parent << start << end;
1025+ Changing c;
1026+ c.parent = parent;
1027+ c.oldSize = model->rowCount ( parent );
1028+ c.last = model->data ( model->index ( start - 1, 0, parent ) );
1029+ c.next = model->data ( model->index ( end + 1, 0, parent ) );
1030+ remove.push ( c );
1031+}
1032+
1033+/*!
1034+ Confirm that what was said was going to happen actually did
1035+
1036+ \sa rowsAboutToBeRemoved()
1037+ */
1038+void ModelTest::rowsRemoved ( const QModelIndex & parent, int start, int end )
1039+{
1040+ qDebug() << "rr" << parent << start << end;
1041+ Changing c = remove.pop();
1042+ QVERIFY( c.parent == parent );
1043+ QVERIFY( c.oldSize - ( end - start + 1 ) == model->rowCount ( parent ) );
1044+ QVERIFY( c.last == model->data ( model->index ( start - 1, 0, c.parent ) ) );
1045+ QVERIFY( c.next == model->data ( model->index ( start, 0, c.parent ) ) );
1046+}
1047+
1048+void ModelTest::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
1049+{
1050+ QVERIFY(topLeft.isValid());
1051+ QVERIFY(bottomRight.isValid());
1052+ QModelIndex commonParent = bottomRight.parent();
1053+ QVERIFY(topLeft.parent() == commonParent);
1054+ QVERIFY(topLeft.row() <= bottomRight.row());
1055+ QVERIFY(topLeft.column() <= bottomRight.column());
1056+ int rowCount = model->rowCount(commonParent);
1057+ int columnCount = model->columnCount(commonParent);
1058+ QVERIFY(bottomRight.row() < rowCount);
1059+ QVERIFY(bottomRight.column() < columnCount);
1060+}
1061+
1062+void ModelTest::headerDataChanged(Qt::Orientation orientation, int start, int end)
1063+{
1064+ QVERIFY(start >= 0);
1065+ QVERIFY(end >= 0);
1066+ QVERIFY(start <= end);
1067+ int itemCount = orientation == Qt::Vertical ? model->rowCount() : model->columnCount();
1068+ QVERIFY(start < itemCount);
1069+ QVERIFY(end < itemCount);
1070+}
1071+
1072
1073=== added file 'tests/plugins/Utils/modeltest.h'
1074--- tests/plugins/Utils/modeltest.h 1970-01-01 00:00:00 +0000
1075+++ tests/plugins/Utils/modeltest.h 2013-04-17 14:04:25 +0000
1076@@ -0,0 +1,101 @@
1077+/****************************************************************************
1078+** The full license is below, this is here to make licensecheck happy
1079+**
1080+** This program is free software; you can redistribute it and/or modify
1081+** it under the terms of the GNU Lesser General Public License as published by
1082+** the Free Software Foundation; version 2.1.
1083+**
1084+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
1085+** Contact: http://www.qt-project.org/legal
1086+**
1087+** This file is part of the test suite of the Qt Toolkit.
1088+**
1089+** $QT_BEGIN_LICENSE:LGPL$
1090+** Commercial License Usage
1091+** Licensees holding valid commercial Qt licenses may use this file in
1092+** accordance with the commercial license agreement provided with the
1093+** Software or, alternatively, in accordance with the terms contained in
1094+** a written agreement between you and Digia. For licensing terms and
1095+** conditions see http://qt.digia.com/licensing. For further information
1096+** use the contact form at http://qt.digia.com/contact-us.
1097+**
1098+** GNU Lesser General Public License Usage
1099+** Alternatively, this file may be used under the terms of the GNU Lesser
1100+** General Public License version 2.1 as published by the Free Software
1101+** Foundation and appearing in the file LICENSE.LGPL included in the
1102+** packaging of this file. Please review the following information to
1103+** ensure the GNU Lesser General Public License version 2.1 requirements
1104+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
1105+**
1106+** In addition, as a special exception, Digia gives you certain additional
1107+** rights. These rights are described in the Digia Qt LGPL Exception
1108+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
1109+**
1110+** GNU General Public License Usage
1111+** Alternatively, this file may be used under the terms of the GNU
1112+** General Public License version 3.0 as published by the Free Software
1113+** Foundation and appearing in the file LICENSE.GPL included in the
1114+** packaging of this file. Please review the following information to
1115+** ensure the GNU General Public License version 3.0 requirements will be
1116+** met: http://www.gnu.org/copyleft/gpl.html.
1117+**
1118+**
1119+** $QT_END_LICENSE$
1120+**
1121+****************************************************************************/
1122+
1123+
1124+#ifndef MODELTEST_H
1125+#define MODELTEST_H
1126+
1127+#include <QtCore/QObject>
1128+#include <QtCore/QAbstractItemModel>
1129+#include <QtCore/QStack>
1130+
1131+class ModelTest : public QObject
1132+{
1133+ Q_OBJECT
1134+
1135+public:
1136+ ModelTest( QAbstractItemModel *model, QObject *parent = 0 );
1137+
1138+private Q_SLOTS:
1139+ void nonDestructiveBasicTest();
1140+ void rowCount();
1141+ void columnCount();
1142+ void hasIndex();
1143+ void index();
1144+ void parent();
1145+ void data();
1146+
1147+protected Q_SLOTS:
1148+ void runAllTests();
1149+ void layoutAboutToBeChanged();
1150+ void layoutChanged();
1151+ void rowsAboutToBeInserted( const QModelIndex &parent, int start, int end );
1152+ void rowsInserted( const QModelIndex & parent, int start, int end );
1153+ void rowsAboutToBeRemoved( const QModelIndex &parent, int start, int end );
1154+ void rowsRemoved( const QModelIndex & parent, int start, int end );
1155+ void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
1156+ void headerDataChanged(Qt::Orientation orientation, int start, int end);
1157+
1158+private:
1159+ void checkChildren( const QModelIndex &parent, int currentDepth = 0 );
1160+
1161+ QAbstractItemModel *model;
1162+
1163+ struct Changing {
1164+ QModelIndex parent;
1165+ int oldSize;
1166+ QVariant last;
1167+ QVariant next;
1168+ };
1169+ QStack<Changing> insert;
1170+ QStack<Changing> remove;
1171+
1172+ bool fetchingMore;
1173+
1174+ QList<QPersistentModelIndex> changing;
1175+};
1176+
1177+#endif
1178
1179=== added file 'tests/plugins/Utils/qlimitproxymodeltest.cpp'
1180--- tests/plugins/Utils/qlimitproxymodeltest.cpp 1970-01-01 00:00:00 +0000
1181+++ tests/plugins/Utils/qlimitproxymodeltest.cpp 2013-04-17 14:04:25 +0000
1182@@ -0,0 +1,472 @@
1183+/*
1184+ * Copyright (C) 2011, 2013 Canonical, Ltd.
1185+ *
1186+ * This program is free software; you can redistribute it and/or modify
1187+ * it under the terms of the GNU General Public License as published by
1188+ * the Free Software Foundation; version 3.
1189+ *
1190+ * This program is distributed in the hope that it will be useful,
1191+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1192+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1193+ * GNU General Public License for more details.
1194+ *
1195+ * You should have received a copy of the GNU General Public License
1196+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1197+ */
1198+
1199+// local
1200+#include "qlimitproxymodelqml.h"
1201+#include "modeltest.h"
1202+
1203+// Qt
1204+#include <QTest>
1205+#include <QSignalSpy>
1206+#include <QModelIndex>
1207+#include <QAbstractListModel>
1208+#include <QDebug>
1209+
1210+
1211+class MockListModel : public QAbstractListModel
1212+{
1213+ Q_OBJECT
1214+
1215+public:
1216+ MockListModel(QObject* parent = 0)
1217+ : QAbstractListModel(parent)
1218+ {
1219+ }
1220+
1221+ int rowCount(const QModelIndex& /* parent */ = QModelIndex()) const
1222+ {
1223+ return m_list.size();
1224+ }
1225+
1226+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const
1227+ {
1228+ if (!index.isValid() || index.row() < 0 || index.row() >= m_list.size() || role != Qt::DisplayRole) {
1229+ return QVariant();
1230+ }
1231+ return QVariant(m_list[index.row()]);
1232+ }
1233+
1234+ QHash<int, QByteArray> roleNames() const
1235+ {
1236+ return m_roles;
1237+ }
1238+
1239+ void setRoles(const QHash<int,QByteArray> &roles) {
1240+ m_roles = roles;
1241+ }
1242+
1243+ bool insertRows(int row, int count, const QModelIndex &parent=QModelIndex()) {
1244+ beginInsertRows(parent, row, row+count-1);
1245+ for (int i=0; i<count; i++) {
1246+ m_list.insert(i+row, "test"+i);
1247+ }
1248+ endInsertRows();
1249+ return true;
1250+ }
1251+
1252+ bool appendRows(QStringList &rows, const QModelIndex &parent=QModelIndex()) {
1253+ beginInsertRows(parent, rowCount(), rowCount() + rows.count() - 1);
1254+ m_list.append(rows);
1255+ endInsertRows();
1256+ return true;
1257+ }
1258+
1259+ bool removeRows(int row, int count, const QModelIndex &parent=QModelIndex()) {
1260+ beginRemoveRows(parent, row, row+count-1);
1261+ for (int i=0; i<count; i++) {
1262+ m_list.removeAt(row);
1263+ }
1264+ endRemoveRows();
1265+ return true;
1266+ }
1267+
1268+private:
1269+ QStringList m_list;
1270+ QHash<int, QByteArray> m_roles;
1271+};
1272+
1273+class QLimitProxyModelTest : public QObject
1274+{
1275+ Q_OBJECT
1276+
1277+private Q_SLOTS:
1278+
1279+ void initTestCase() {
1280+ qRegisterMetaType<QModelIndex>("QModelIndex");
1281+ }
1282+
1283+ void testRoleNamesSetAfter()
1284+ {
1285+ QLimitProxyModelQML proxy;
1286+ MockListModel model;
1287+ QHash<int, QByteArray> roles;
1288+
1289+ proxy.setModel(&model);
1290+
1291+ roles[0] = "role0";
1292+ roles[1] = "role1";
1293+ model.setRoles(roles);
1294+ QCOMPARE(model.roleNames(), proxy.roleNames());
1295+ }
1296+
1297+ void testRoleNamesSetBefore()
1298+ {
1299+ QLimitProxyModelQML proxy;
1300+ MockListModel model;
1301+ QHash<int, QByteArray> roles;
1302+
1303+ roles[0] = "role0";
1304+ roles[1] = "role1";
1305+ model.setRoles(roles);
1306+
1307+ proxy.setModel(&model);
1308+ QCOMPARE(model.roleNames(), proxy.roleNames());
1309+ }
1310+
1311+ void testCountSetAfter()
1312+ {
1313+ QLimitProxyModelQML proxy;
1314+ MockListModel model;
1315+ model.insertRows(0, 5);
1316+
1317+ QSignalSpy spyOnCountChanged(&proxy, SIGNAL(countChanged()));
1318+
1319+ proxy.setModel(&model);
1320+ QCOMPARE(proxy.rowCount(), 5);
1321+ QVERIFY(spyOnCountChanged.count() >= 1);
1322+ }
1323+
1324+ void testCountInsert()
1325+ {
1326+ QLimitProxyModelQML proxy;
1327+ MockListModel model;
1328+
1329+ proxy.setModel(&model);
1330+
1331+ QSignalSpy spyOnCountChanged(&proxy, SIGNAL(countChanged()));
1332+
1333+ model.insertRows(0, 5);
1334+ QCOMPARE(proxy.rowCount(), 5);
1335+ QCOMPARE(spyOnCountChanged.count(), 1);
1336+ }
1337+
1338+ void testCountRemove()
1339+ {
1340+ QLimitProxyModelQML proxy;
1341+ MockListModel model;
1342+ model.insertRows(0, 5);
1343+
1344+ proxy.setModel(&model);
1345+
1346+ QSignalSpy spyOnCountChanged(&proxy, SIGNAL(countChanged()));
1347+
1348+ model.removeRows(0, 3);
1349+ QCOMPARE(proxy.rowCount(), 2);
1350+ QCOMPARE(spyOnCountChanged.count(), 1);
1351+ }
1352+
1353+ void testLimitCount()
1354+ {
1355+ QLimitProxyModelQML proxy;
1356+ MockListModel model;
1357+
1358+ proxy.setModel(&model);
1359+ proxy.setLimit(3);
1360+
1361+ QSignalSpy spyOnCountChanged(&proxy, SIGNAL(countChanged()));
1362+
1363+ model.insertRows(0, 5);
1364+ QCOMPARE(proxy.rowCount(), 3);
1365+ QCOMPARE(spyOnCountChanged.count(), 1);
1366+ }
1367+
1368+ void testLimitLesserThanCount()
1369+ {
1370+ QLimitProxyModelQML proxy;
1371+ MockListModel model;
1372+ QList<QVariant> arguments;
1373+ model.insertRows(0, 10);
1374+
1375+ proxy.setModel(&model);
1376+
1377+ QSignalSpy spyOnRowsRemoved(&proxy, SIGNAL(rowsRemoved(const QModelIndex &, int, int)));
1378+ QSignalSpy spyOnRowsInserted(&proxy, SIGNAL(rowsInserted(const QModelIndex &, int, int)));
1379+ QSignalSpy spyOnCountChanged(&proxy, SIGNAL(countChanged()));
1380+
1381+ proxy.setLimit(5);
1382+ QCOMPARE(spyOnRowsInserted.count(), 0);
1383+ QCOMPARE(spyOnCountChanged.count(), 1);
1384+ QCOMPARE(spyOnRowsRemoved.count(), 1);
1385+ arguments = spyOnRowsRemoved.takeFirst();
1386+ QCOMPARE(arguments.at(1).toInt(), 5);
1387+ QCOMPARE(arguments.at(2).toInt(), 9);
1388+ QCOMPARE(proxy.rowCount(), 5);
1389+ spyOnRowsRemoved.clear();
1390+ spyOnCountChanged.clear();
1391+
1392+ proxy.setLimit(7);
1393+ QCOMPARE(spyOnRowsInserted.count(), 1);
1394+ QCOMPARE(spyOnRowsRemoved.count(), 0);
1395+ QCOMPARE(spyOnCountChanged.count(), 1);
1396+ arguments = spyOnRowsInserted.takeFirst();
1397+ QCOMPARE(arguments.at(1).toInt(), 5);
1398+ QCOMPARE(arguments.at(2).toInt(), 6);
1399+ QCOMPARE(proxy.rowCount(), 7);
1400+ spyOnRowsInserted.clear();
1401+ spyOnCountChanged.clear();
1402+
1403+ proxy.setLimit(3);
1404+ QCOMPARE(spyOnRowsRemoved.count(), 1);
1405+ QCOMPARE(spyOnRowsInserted.count(), 0);
1406+ QCOMPARE(spyOnCountChanged.count(), 1);
1407+ arguments = spyOnRowsRemoved.takeFirst();
1408+ QCOMPARE(arguments.at(1).toInt(), 3);
1409+ QCOMPARE(arguments.at(2).toInt(), 6);
1410+ QCOMPARE(proxy.rowCount(), 3);
1411+ spyOnRowsRemoved.clear();
1412+ spyOnCountChanged.clear();
1413+ }
1414+
1415+ void testLimitGreaterThanCount()
1416+ {
1417+ QLimitProxyModelQML proxy;
1418+ MockListModel model;
1419+ QList<QVariant> arguments;
1420+ model.insertRows(0, 5);
1421+
1422+ proxy.setModel(&model);
1423+
1424+ QSignalSpy spyOnRowsRemoved(&proxy, SIGNAL(rowsRemoved(const QModelIndex &, int, int)));
1425+ QSignalSpy spyOnRowsInserted(&proxy, SIGNAL(rowsInserted(const QModelIndex &, int, int)));
1426+ QSignalSpy spyOnCountChanged(&proxy, SIGNAL(countChanged()));
1427+
1428+ proxy.setLimit(7);
1429+ QCOMPARE(spyOnRowsRemoved.count(), 0);
1430+ QCOMPARE(spyOnRowsInserted.count(), 0);
1431+ QCOMPARE(spyOnCountChanged.count(), 0);
1432+ QCOMPARE(proxy.rowCount(), 5);
1433+
1434+ proxy.setLimit(5);
1435+ QCOMPARE(spyOnRowsRemoved.count(), 0);
1436+ QCOMPARE(spyOnRowsInserted.count(), 0);
1437+ QCOMPARE(spyOnCountChanged.count(), 0);
1438+ QCOMPARE(proxy.rowCount(), 5);
1439+
1440+ proxy.setLimit(3);
1441+ QCOMPARE(spyOnRowsInserted.count(), 0);
1442+ QCOMPARE(spyOnRowsRemoved.count(), 1);
1443+ QCOMPARE(spyOnCountChanged.count(), 1);
1444+ arguments = spyOnRowsRemoved.takeFirst();
1445+ QCOMPARE(arguments.at(1).toInt(), 3);
1446+ QCOMPARE(arguments.at(2).toInt(), 4);
1447+ QCOMPARE(proxy.rowCount(), 3);
1448+ spyOnRowsRemoved.clear();
1449+ spyOnCountChanged.clear();
1450+
1451+ proxy.setLimit(4);
1452+ QCOMPARE(spyOnRowsRemoved.count(), 0);
1453+ QCOMPARE(spyOnRowsInserted.count(), 1);
1454+ QCOMPARE(spyOnCountChanged.count(), 1);
1455+ arguments = spyOnRowsInserted.takeFirst();
1456+ QCOMPARE(arguments.at(1).toInt(), 3);
1457+ QCOMPARE(arguments.at(2).toInt(), 3);
1458+ QCOMPARE(proxy.rowCount(), 4);
1459+ spyOnRowsInserted.clear();
1460+ spyOnCountChanged.clear();
1461+
1462+ proxy.setLimit(7);
1463+ QCOMPARE(spyOnRowsRemoved.count(), 0);
1464+ QCOMPARE(spyOnRowsInserted.count(), 1);
1465+ QCOMPARE(spyOnCountChanged.count(), 1);
1466+ arguments = spyOnRowsInserted.takeFirst();
1467+ QCOMPARE(arguments.at(1).toInt(), 4);
1468+ QCOMPARE(arguments.at(2).toInt(), 4);
1469+ QCOMPARE(proxy.rowCount(), 5);
1470+ spyOnRowsInserted.clear();
1471+ spyOnCountChanged.clear();
1472+ }
1473+
1474+ void testLimitMinusOne()
1475+ {
1476+ QLimitProxyModelQML proxy;
1477+ MockListModel model;
1478+ QList<QVariant> arguments;
1479+ model.insertRows(0, 5);
1480+
1481+ proxy.setModel(&model);
1482+
1483+ QSignalSpy spyOnRowsRemoved(&proxy, SIGNAL(rowsRemoved(const QModelIndex &, int, int)));
1484+ QSignalSpy spyOnRowsInserted(&proxy, SIGNAL(rowsInserted(const QModelIndex &, int, int)));
1485+ QSignalSpy spyOnCountChanged(&proxy, SIGNAL(countChanged()));
1486+
1487+ proxy.setLimit(7);
1488+ QCOMPARE(spyOnRowsRemoved.count(), 0);
1489+ QCOMPARE(spyOnRowsInserted.count(), 0);
1490+ QCOMPARE(spyOnCountChanged.count(), 0);
1491+ QCOMPARE(proxy.rowCount(), 5);
1492+
1493+ proxy.setLimit(-1);
1494+ QCOMPARE(spyOnRowsRemoved.count(), 0);
1495+ QCOMPARE(spyOnRowsInserted.count(), 0);
1496+ QCOMPARE(spyOnCountChanged.count(), 0);
1497+ QCOMPARE(proxy.rowCount(), 5);
1498+
1499+ proxy.setLimit(3);
1500+ QCOMPARE(spyOnRowsInserted.count(), 0);
1501+ QCOMPARE(spyOnRowsRemoved.count(), 1);
1502+ QCOMPARE(spyOnCountChanged.count(), 1);
1503+ arguments = spyOnRowsRemoved.takeFirst();
1504+ QCOMPARE(arguments.at(1).toInt(), 3);
1505+ QCOMPARE(arguments.at(2).toInt(), 4);
1506+ QCOMPARE(proxy.rowCount(), 3);
1507+ spyOnRowsRemoved.clear();
1508+ spyOnCountChanged.clear();
1509+
1510+ proxy.setLimit(-1);
1511+ QCOMPARE(spyOnRowsRemoved.count(), 0);
1512+ QCOMPARE(spyOnRowsInserted.count(), 1);
1513+ QCOMPARE(spyOnCountChanged.count(), 1);
1514+ arguments = spyOnRowsInserted.takeFirst();
1515+ QCOMPARE(arguments.at(1).toInt(), 3);
1516+ QCOMPARE(arguments.at(2).toInt(), 4);
1517+ QCOMPARE(proxy.rowCount(), 5);
1518+ spyOnRowsInserted.clear();
1519+ spyOnCountChanged.clear();
1520+ }
1521+
1522+ void testLimitInsert() {
1523+ QLimitProxyModelQML proxy;
1524+ MockListModel model;
1525+ QList<QVariant> arguments;
1526+
1527+ proxy.setModel(&model);
1528+ proxy.setLimit(7);
1529+
1530+ QSignalSpy spyOnRowsRemoved(&proxy, SIGNAL(rowsRemoved(const QModelIndex &, int, int)));
1531+ QSignalSpy spyOnRowsInserted(&proxy, SIGNAL(rowsInserted(const QModelIndex &, int, int)));
1532+ QSignalSpy spyOnCountChanged(&proxy, SIGNAL(countChanged()));
1533+
1534+ model.insertRows(0, 5);
1535+ QCOMPARE(spyOnRowsRemoved.count(), 0);
1536+ QCOMPARE(spyOnRowsInserted.count(), 1);
1537+ QCOMPARE(spyOnCountChanged.count(), 1);
1538+ arguments = spyOnRowsInserted.takeFirst();
1539+ QCOMPARE(arguments.at(1).toInt(), 0);
1540+ QCOMPARE(arguments.at(2).toInt(), 4);
1541+ QCOMPARE(proxy.rowCount(), 5);
1542+ spyOnRowsInserted.clear();
1543+ spyOnCountChanged.clear();
1544+
1545+ model.insertRows(2, 2);
1546+ QCOMPARE(spyOnRowsRemoved.count(), 0);
1547+ QCOMPARE(spyOnRowsInserted.count(), 1);
1548+ QCOMPARE(spyOnCountChanged.count(), 1);
1549+ arguments = spyOnRowsInserted.takeFirst();
1550+ QCOMPARE(arguments.at(1).toInt(), 2);
1551+ QCOMPARE(arguments.at(2).toInt(), 3);
1552+ QCOMPARE(proxy.rowCount(), 7);
1553+ spyOnRowsInserted.clear();
1554+ spyOnCountChanged.clear();
1555+
1556+ model.insertRows(7, 3);
1557+ QCOMPARE(proxy.rowCount(), 7);
1558+ QCOMPARE(spyOnRowsRemoved.count(), 0);
1559+ QCOMPARE(spyOnRowsInserted.count(), 0);
1560+ QCOMPARE(spyOnCountChanged.count(), 0);
1561+ }
1562+
1563+ void testLimitRemove() {
1564+ QLimitProxyModelQML proxy;
1565+ MockListModel model;
1566+ QList<QVariant> arguments;
1567+
1568+ proxy.setModel(&model);
1569+ proxy.setLimit(7);
1570+
1571+ model.insertRows(0, 12);
1572+
1573+ QCOMPARE(proxy.rowCount(), 7);
1574+
1575+ QSignalSpy spyOnRowsRemoved(&proxy, SIGNAL(rowsRemoved(const QModelIndex &, int, int)));
1576+ QSignalSpy spyOnRowsInserted(&proxy, SIGNAL(rowsInserted(const QModelIndex &, int, int)));
1577+ QSignalSpy spyOnCountChanged(&proxy, SIGNAL(countChanged()));
1578+
1579+ model.removeRows(7, 3);
1580+ QCOMPARE(proxy.rowCount(), 7);
1581+ QCOMPARE(spyOnRowsRemoved.count(), 0);
1582+ QCOMPARE(spyOnRowsInserted.count(), 0);
1583+ QCOMPARE(spyOnCountChanged.count(), 0);
1584+
1585+ model.removeRows(2, 2);
1586+ QCOMPARE(spyOnRowsRemoved.count(), 1);
1587+ QCOMPARE(spyOnRowsInserted.count(), 0);
1588+ QCOMPARE(spyOnCountChanged.count(), 1);
1589+ arguments = spyOnRowsRemoved.takeFirst();
1590+ QCOMPARE(arguments.at(1).toInt(), 2);
1591+ QCOMPARE(arguments.at(2).toInt(), 3);
1592+ QCOMPARE(proxy.rowCount(), 7);
1593+ spyOnRowsInserted.clear();
1594+ spyOnCountChanged.clear();
1595+
1596+ model.removeRows(0, 7);
1597+ QCOMPARE(spyOnRowsRemoved.count(), 1);
1598+ QCOMPARE(spyOnRowsInserted.count(), 0);
1599+ QCOMPARE(spyOnCountChanged.count(), 1);
1600+ arguments = spyOnRowsRemoved.takeFirst();
1601+ QCOMPARE(arguments.at(1).toInt(), 0);
1602+ QCOMPARE(arguments.at(2).toInt(), 6);
1603+ QCOMPARE(proxy.rowCount(), 0);
1604+ }
1605+
1606+ void testNestedProxyRoleNames() {
1607+ QLimitProxyModelQML proxy1, proxy2;
1608+ MockListModel model;
1609+ QHash<int, QByteArray> roles;
1610+ roles[0] = "role0";
1611+ roles[1] = "role1";
1612+ model.setRoles(roles);
1613+
1614+ proxy1.setModel(&model);
1615+ proxy2.setModel(&proxy1);
1616+
1617+ QCOMPARE(proxy2.roleNames(), model.roleNames());
1618+ }
1619+
1620+ void testModelTest() {
1621+ QLimitProxyModelQML proxy;
1622+ MockListModel model;
1623+
1624+ proxy.setModel(&model);
1625+ proxy.setLimit(7);
1626+
1627+ model.insertRows(0, 12);
1628+
1629+ ModelTest t1(&proxy);
1630+ }
1631+ void testModelChanged() {
1632+ QLimitProxyModelQML proxy;
1633+ MockListModel model, model2;
1634+
1635+ QSignalSpy spyOnModelChanged(&proxy, SIGNAL(modelChanged()));
1636+
1637+ proxy.setModel(&model);
1638+ QCOMPARE(spyOnModelChanged.count(), 1);
1639+
1640+ proxy.setModel(&model2);
1641+ QCOMPARE(spyOnModelChanged.count(), 2);
1642+
1643+ proxy.setModel(&model);
1644+ QCOMPARE(spyOnModelChanged.count(), 3);
1645+
1646+ proxy.setModel(&model);
1647+ QCOMPARE(spyOnModelChanged.count(), 3);
1648+ }
1649+};
1650+
1651+QTEST_MAIN(QLimitProxyModelTest)
1652+
1653+#include "qlimitproxymodeltest.moc"
1654+
1655
1656=== modified file 'tests/plugins/Utils/qsortfilterproxymodeltest.cpp'
1657--- tests/plugins/Utils/qsortfilterproxymodeltest.cpp 2013-04-12 07:59:00 +0000
1658+++ tests/plugins/Utils/qsortfilterproxymodeltest.cpp 2013-04-17 14:04:25 +0000
1659@@ -19,6 +19,7 @@
1660
1661 // local
1662 #include "qsortfilterproxymodelqml.h"
1663+#include "modeltest.h"
1664
1665 // Qt
1666 #include <QTest>
1667@@ -45,9 +46,7 @@
1668
1669 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const
1670 {
1671- Q_UNUSED(role)
1672-
1673- if (!index.isValid() || index.row() < 0 || index.row() >= m_list.size()) {
1674+ if (!index.isValid() || index.row() < 0 || index.row() >= m_list.size() || role != Qt::DisplayRole) {
1675 return QVariant();
1676 }
1677 return QVariant(m_list[index.row()]);
1678@@ -83,7 +82,7 @@
1679 for (int i=0; i<count; i++) {
1680 m_list.removeAt(row);
1681 }
1682- endInsertRows();
1683+ endRemoveRows();
1684 return true;
1685 }
1686
1687@@ -164,6 +163,7 @@
1688 model.insertRows(0, 5);
1689
1690 proxy.setModel(&model);
1691+ QCOMPARE(proxy.count(), 5);
1692
1693 QSignalSpy spyOnCountChanged(&proxy, SIGNAL(countChanged()));
1694
1695@@ -172,221 +172,6 @@
1696 QCOMPARE(spyOnCountChanged.count(), 1);
1697 }
1698
1699- void testLimitCount()
1700- {
1701- QSortFilterProxyModelQML proxy;
1702- MockListModel model;
1703-
1704- proxy.setModel(&model);
1705- proxy.setLimit(3);
1706-
1707- QSignalSpy spyOnCountChanged(&proxy, SIGNAL(countChanged()));
1708-
1709- model.insertRows(0, 5);
1710- QCOMPARE(proxy.count(), 3);
1711- QCOMPARE(spyOnCountChanged.count(), 1);
1712- }
1713-
1714- void testLimitLesserThanCount()
1715- {
1716- QSortFilterProxyModelQML proxy;
1717- MockListModel model;
1718- QList<QVariant> arguments;
1719- model.insertRows(0, 10);
1720-
1721- proxy.setModel(&model);
1722-
1723- QSignalSpy spyOnRowsRemoved(&proxy, SIGNAL(rowsRemoved(const QModelIndex &, int, int)));
1724- QSignalSpy spyOnRowsInserted(&proxy, SIGNAL(rowsInserted(const QModelIndex &, int, int)));
1725- QSignalSpy spyOnCountChanged(&proxy, SIGNAL(countChanged()));
1726-
1727- /* FIXME: for some reason the rowsRemoved is not emitted if
1728- proxy.rowCount is not called before.
1729- */
1730- proxy.rowCount();
1731- proxy.setLimit(5);
1732- QCOMPARE(spyOnRowsInserted.count(), 0);
1733- QCOMPARE(spyOnCountChanged.count(), 1);
1734- QCOMPARE(spyOnRowsRemoved.count(), 1);
1735- arguments = spyOnRowsRemoved.takeFirst();
1736- QCOMPARE(arguments.at(1).toInt(), 5);
1737- QCOMPARE(arguments.at(2).toInt(), 9);
1738- QCOMPARE(proxy.count(), 5);
1739- spyOnRowsRemoved.clear();
1740- spyOnCountChanged.clear();
1741-
1742- proxy.setLimit(7);
1743- QCOMPARE(spyOnRowsInserted.count(), 1);
1744- QCOMPARE(spyOnRowsRemoved.count(), 0);
1745- QCOMPARE(spyOnCountChanged.count(), 1);
1746- arguments = spyOnRowsInserted.takeFirst();
1747- QCOMPARE(arguments.at(1).toInt(), 5);
1748- QCOMPARE(arguments.at(2).toInt(), 6);
1749- QCOMPARE(proxy.count(), 7);
1750- spyOnRowsInserted.clear();
1751- spyOnCountChanged.clear();
1752-
1753- proxy.setLimit(3);
1754- QCOMPARE(spyOnRowsRemoved.count(), 1);
1755- QCOMPARE(spyOnRowsInserted.count(), 0);
1756- QCOMPARE(spyOnCountChanged.count(), 1);
1757- arguments = spyOnRowsRemoved.takeFirst();
1758- QCOMPARE(arguments.at(1).toInt(), 3);
1759- QCOMPARE(arguments.at(2).toInt(), 6);
1760- QCOMPARE(proxy.count(), 3);
1761- spyOnRowsRemoved.clear();
1762- spyOnCountChanged.clear();
1763- }
1764-
1765- void testLimitGreaterThanCount()
1766- {
1767- QSortFilterProxyModelQML proxy;
1768- MockListModel model;
1769- QList<QVariant> arguments;
1770- model.insertRows(0, 5);
1771-
1772- proxy.setModel(&model);
1773-
1774- QSignalSpy spyOnRowsRemoved(&proxy, SIGNAL(rowsRemoved(const QModelIndex &, int, int)));
1775- QSignalSpy spyOnRowsInserted(&proxy, SIGNAL(rowsInserted(const QModelIndex &, int, int)));
1776- QSignalSpy spyOnCountChanged(&proxy, SIGNAL(countChanged()));
1777-
1778- proxy.setLimit(7);
1779- QCOMPARE(spyOnRowsRemoved.count(), 0);
1780- QCOMPARE(spyOnRowsInserted.count(), 0);
1781- QCOMPARE(spyOnCountChanged.count(), 0);
1782- QCOMPARE(proxy.count(), 5);
1783-
1784- proxy.setLimit(5);
1785- QCOMPARE(spyOnRowsRemoved.count(), 0);
1786- QCOMPARE(spyOnRowsInserted.count(), 0);
1787- QCOMPARE(spyOnCountChanged.count(), 0);
1788- QCOMPARE(proxy.count(), 5);
1789-
1790- proxy.setLimit(3);
1791- QCOMPARE(spyOnRowsInserted.count(), 0);
1792- QCOMPARE(spyOnRowsRemoved.count(), 1);
1793- QCOMPARE(spyOnCountChanged.count(), 1);
1794- arguments = spyOnRowsRemoved.takeFirst();
1795- QCOMPARE(arguments.at(1).toInt(), 3);
1796- QCOMPARE(arguments.at(2).toInt(), 4);
1797- QCOMPARE(proxy.count(), 3);
1798- spyOnRowsRemoved.clear();
1799- spyOnCountChanged.clear();
1800-
1801- proxy.setLimit(4);
1802- QCOMPARE(spyOnRowsRemoved.count(), 0);
1803- QCOMPARE(spyOnRowsInserted.count(), 1);
1804- QCOMPARE(spyOnCountChanged.count(), 1);
1805- arguments = spyOnRowsInserted.takeFirst();
1806- QCOMPARE(arguments.at(1).toInt(), 3);
1807- QCOMPARE(arguments.at(2).toInt(), 3);
1808- QCOMPARE(proxy.count(), 4);
1809- spyOnRowsInserted.clear();
1810- spyOnCountChanged.clear();
1811-
1812- proxy.setLimit(7);
1813- QCOMPARE(spyOnRowsRemoved.count(), 0);
1814- QCOMPARE(spyOnRowsInserted.count(), 1);
1815- QCOMPARE(spyOnCountChanged.count(), 1);
1816- arguments = spyOnRowsInserted.takeFirst();
1817- QCOMPARE(arguments.at(1).toInt(), 4);
1818- QCOMPARE(arguments.at(2).toInt(), 4);
1819- QCOMPARE(proxy.count(), 5);
1820- spyOnRowsInserted.clear();
1821- spyOnCountChanged.clear();
1822- }
1823-
1824- void testLimitMinusOne()
1825- {
1826- QSortFilterProxyModelQML proxy;
1827- MockListModel model;
1828- QList<QVariant> arguments;
1829- model.insertRows(0, 5);
1830-
1831- proxy.setModel(&model);
1832-
1833- QSignalSpy spyOnRowsRemoved(&proxy, SIGNAL(rowsRemoved(const QModelIndex &, int, int)));
1834- QSignalSpy spyOnRowsInserted(&proxy, SIGNAL(rowsInserted(const QModelIndex &, int, int)));
1835- QSignalSpy spyOnCountChanged(&proxy, SIGNAL(countChanged()));
1836-
1837- proxy.setLimit(7);
1838- QCOMPARE(spyOnRowsRemoved.count(), 0);
1839- QCOMPARE(spyOnRowsInserted.count(), 0);
1840- QCOMPARE(spyOnCountChanged.count(), 0);
1841- QCOMPARE(proxy.count(), 5);
1842-
1843- proxy.setLimit(-1);
1844- QCOMPARE(spyOnRowsRemoved.count(), 0);
1845- QCOMPARE(spyOnRowsInserted.count(), 0);
1846- QCOMPARE(spyOnCountChanged.count(), 0);
1847- QCOMPARE(proxy.count(), 5);
1848-
1849- proxy.setLimit(3);
1850- QCOMPARE(spyOnRowsInserted.count(), 0);
1851- QCOMPARE(spyOnRowsRemoved.count(), 1);
1852- QCOMPARE(spyOnCountChanged.count(), 1);
1853- arguments = spyOnRowsRemoved.takeFirst();
1854- QCOMPARE(arguments.at(1).toInt(), 3);
1855- QCOMPARE(arguments.at(2).toInt(), 4);
1856- QCOMPARE(proxy.count(), 3);
1857- spyOnRowsRemoved.clear();
1858- spyOnCountChanged.clear();
1859-
1860- proxy.setLimit(-1);
1861- QCOMPARE(spyOnRowsRemoved.count(), 0);
1862- QCOMPARE(spyOnRowsInserted.count(), 1);
1863- QCOMPARE(spyOnCountChanged.count(), 1);
1864- arguments = spyOnRowsInserted.takeFirst();
1865- QCOMPARE(arguments.at(1).toInt(), 3);
1866- QCOMPARE(arguments.at(2).toInt(), 4);
1867- QCOMPARE(proxy.count(), 5);
1868- spyOnRowsInserted.clear();
1869- spyOnCountChanged.clear();
1870- }
1871-
1872- void testLimitInsert() {
1873- QSortFilterProxyModelQML proxy;
1874- MockListModel model;
1875- QList<QVariant> arguments;
1876-
1877- proxy.setModel(&model);
1878- proxy.setLimit(7);
1879-
1880- QSignalSpy spyOnRowsRemoved(&proxy, SIGNAL(rowsRemoved(const QModelIndex &, int, int)));
1881- QSignalSpy spyOnRowsInserted(&proxy, SIGNAL(rowsInserted(const QModelIndex &, int, int)));
1882- QSignalSpy spyOnCountChanged(&proxy, SIGNAL(countChanged()));
1883-
1884- model.insertRows(0, 5);
1885- QCOMPARE(spyOnRowsRemoved.count(), 0);
1886- QCOMPARE(spyOnRowsInserted.count(), 1);
1887- QCOMPARE(spyOnCountChanged.count(), 1);
1888- arguments = spyOnRowsInserted.takeFirst();
1889- QCOMPARE(arguments.at(1).toInt(), 0);
1890- QCOMPARE(arguments.at(2).toInt(), 4);
1891- QCOMPARE(proxy.count(), 5);
1892- spyOnRowsInserted.clear();
1893- spyOnCountChanged.clear();
1894-
1895- model.insertRows(2, 2);
1896- QCOMPARE(spyOnRowsRemoved.count(), 0);
1897- QCOMPARE(spyOnRowsInserted.count(), 1);
1898- QCOMPARE(spyOnCountChanged.count(), 1);
1899- arguments = spyOnRowsInserted.takeFirst();
1900- QCOMPARE(arguments.at(1).toInt(), 2);
1901- QCOMPARE(arguments.at(2).toInt(), 3);
1902- QCOMPARE(proxy.count(), 7);
1903- spyOnRowsInserted.clear();
1904- spyOnCountChanged.clear();
1905-
1906- /* FIXME: failing case */
1907- model.insertRows(5, 3);
1908- //QCOMPARE(proxy.count(), 7); // proxy.count == 9
1909- QCOMPARE(spyOnRowsRemoved.count(), 0);
1910- //QCOMPARE(spyOnRowsInserted.count(), 0); // spyOnRowsInserted.count == 1
1911- //QCOMPARE(spyOnCountChanged.count(), 0); // spyOnCountChanged.count == 1
1912- }
1913-
1914 void testInvertMatch() {
1915 QSortFilterProxyModelQML proxy;
1916 MockListModel model;
1917@@ -442,6 +227,42 @@
1918
1919 QCOMPARE(proxy2.roleNames(), model.roleNames());
1920 }
1921+
1922+ void testModelTest() {
1923+ QSortFilterProxyModelQML proxy;
1924+ MockListModel model;
1925+
1926+ proxy.setModel(&model);
1927+ proxy.setDynamicSortFilter(true);
1928+
1929+ QStringList rows;
1930+ rows << "a/foobar/b" << "foobar" << "foobarbaz" << "hello";
1931+ model.appendRows(rows);
1932+
1933+ proxy.setInvertMatch(true);
1934+ proxy.setFilterRegExp("^foobar$");
1935+
1936+ ModelTest t1(&proxy);
1937+ }
1938+
1939+ void testModelChanged() {
1940+ QSortFilterProxyModelQML proxy;
1941+ MockListModel model, model2;
1942+
1943+ QSignalSpy spyOnModelChanged(&proxy, SIGNAL(modelChanged()));
1944+
1945+ proxy.setModel(&model);
1946+ QCOMPARE(spyOnModelChanged.count(), 1);
1947+
1948+ proxy.setModel(&model2);
1949+ QCOMPARE(spyOnModelChanged.count(), 2);
1950+
1951+ proxy.setModel(&model);
1952+ QCOMPARE(spyOnModelChanged.count(), 3);
1953+
1954+ proxy.setModel(&model);
1955+ QCOMPARE(spyOnModelChanged.count(), 3);
1956+ }
1957 };
1958
1959 QTEST_MAIN(QSortFilterProxyModelTest)

Subscribers

People subscribed via source and target branches