1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the test suite of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
21 | ** included in the packaging of this file. Please review the following |
22 | ** information to ensure the GNU General Public License requirements will |
23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
24 | ** |
25 | ** $QT_END_LICENSE$ |
26 | ** |
27 | ****************************************************************************/ |
28 | |
29 | #include <QtTest/QtTest> |
30 | #include <QtGui/QtGui> |
31 | #include <QtWidgets/QtWidgets> |
32 | |
33 | #include "dynamictreemodel.h" |
34 | |
35 | class tst_QAbstractItemModelTester : public QObject |
36 | { |
37 | Q_OBJECT |
38 | |
39 | private slots: |
40 | void stringListModel(); |
41 | void treeWidgetModel(); |
42 | void standardItemModel(); |
43 | void standardItemModelZeroColumns(); |
44 | void testInsertThroughProxy(); |
45 | void moveSourceItems(); |
46 | void testResetThroughProxy(); |
47 | }; |
48 | |
49 | /* |
50 | tests |
51 | */ |
52 | |
53 | void tst_QAbstractItemModelTester::stringListModel() |
54 | { |
55 | QStringListModel model; |
56 | QSortFilterProxyModel proxy; |
57 | |
58 | QAbstractItemModelTester t1(&model); |
59 | QAbstractItemModelTester t2(&proxy); |
60 | |
61 | proxy.setSourceModel(&model); |
62 | |
63 | model.setStringList(QStringList() << "2" << "3" << "1" ); |
64 | model.setStringList(QStringList() << "a" << "e" << "plop" << "b" << "c" ); |
65 | |
66 | proxy.setDynamicSortFilter(true); |
67 | proxy.setFilterRegularExpression(QRegularExpression("[^b]" )); |
68 | } |
69 | |
70 | void tst_QAbstractItemModelTester::treeWidgetModel() |
71 | { |
72 | QTreeWidget widget; |
73 | |
74 | QAbstractItemModelTester t1(widget.model()); |
75 | |
76 | QTreeWidgetItem *root = new QTreeWidgetItem(&widget, QStringList("root" )); |
77 | for (int i = 0; i < 20; ++i) |
78 | new QTreeWidgetItem(root, QStringList(QString::number(i))); |
79 | QTreeWidgetItem *remove = root->child(index: 2); |
80 | root->removeChild(child: remove); |
81 | QTreeWidgetItem *parent = new QTreeWidgetItem(&widget, QStringList("parent" )); |
82 | new QTreeWidgetItem(parent, QStringList("child" )); |
83 | parent->setHidden(true); |
84 | |
85 | widget.sortByColumn(column: 0, order: Qt::AscendingOrder); |
86 | } |
87 | |
88 | void tst_QAbstractItemModelTester::standardItemModel() |
89 | { |
90 | QStandardItemModel model(10, 10); |
91 | QSortFilterProxyModel proxy; |
92 | |
93 | QAbstractItemModelTester t1(&model); |
94 | QAbstractItemModelTester t2(&proxy); |
95 | |
96 | proxy.setSourceModel(&model); |
97 | |
98 | model.insertRows(row: 2, count: 5); |
99 | model.removeRows(row: 4, count: 5); |
100 | |
101 | model.insertColumns(column: 2, count: 5); |
102 | model.removeColumns(column: 4, count: 5); |
103 | |
104 | model.insertRows(row: 0, count: 5, parent: model.index(row: 1, column: 1)); |
105 | model.insertColumns(column: 0, count: 5, parent: model.index(row: 1, column: 3)); |
106 | } |
107 | |
108 | void tst_QAbstractItemModelTester::standardItemModelZeroColumns() |
109 | { |
110 | QStandardItemModel model; |
111 | QAbstractItemModelTester t1(&model); |
112 | // QTBUG-92220 |
113 | model.insertRows(row: 0, count: 5); |
114 | model.removeRows(row: 0, count: 5); |
115 | // QTBUG-92886 |
116 | model.insertRows(row: 0, count: 5); |
117 | model.removeRows(row: 1, count: 2); |
118 | } |
119 | |
120 | void tst_QAbstractItemModelTester::testInsertThroughProxy() |
121 | { |
122 | DynamicTreeModel *model = new DynamicTreeModel(this); |
123 | |
124 | QSortFilterProxyModel *proxy = new QSortFilterProxyModel(this); |
125 | proxy->setSourceModel(model); |
126 | |
127 | new QAbstractItemModelTester(proxy, this); |
128 | |
129 | ModelInsertCommand *insertCommand = new ModelInsertCommand(model, this); |
130 | insertCommand->setNumCols(4); |
131 | insertCommand->setStartRow(0); |
132 | insertCommand->setEndRow(9); |
133 | // Parent is QModelIndex() |
134 | insertCommand->doCommand(); |
135 | |
136 | insertCommand = new ModelInsertCommand(model, this); |
137 | insertCommand->setNumCols(4); |
138 | insertCommand->setAncestorRowNumbers(QList<int>() << 5); |
139 | insertCommand->setStartRow(0); |
140 | insertCommand->setEndRow(9); |
141 | insertCommand->doCommand(); |
142 | |
143 | ModelMoveCommand *moveCommand = new ModelMoveCommand(model, this); |
144 | moveCommand->setNumCols(4); |
145 | moveCommand->setStartRow(0); |
146 | moveCommand->setEndRow(0); |
147 | moveCommand->setDestRow(9); |
148 | moveCommand->setDestAncestors(QList<int>() << 5); |
149 | moveCommand->doCommand(); |
150 | } |
151 | |
152 | /** |
153 | Makes the persistent index list publicly accessible |
154 | */ |
155 | class AccessibleProxyModel : public QSortFilterProxyModel |
156 | { |
157 | Q_OBJECT |
158 | public: |
159 | AccessibleProxyModel(QObject *parent = 0) : QSortFilterProxyModel(parent) |
160 | { |
161 | } |
162 | |
163 | QModelIndexList persistent() |
164 | { |
165 | return persistentIndexList(); |
166 | } |
167 | }; |
168 | |
169 | class ObservingObject : public QObject |
170 | { |
171 | Q_OBJECT |
172 | public: |
173 | ObservingObject(AccessibleProxyModel *proxy, QObject *parent = 0) : |
174 | QObject(parent), |
175 | m_proxy(proxy), |
176 | storePersistentFailureCount(0), |
177 | checkPersistentFailureCount(0) |
178 | { |
179 | connect(asender: m_proxy, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)), |
180 | SLOT(storePersistent())); |
181 | connect(asender: m_proxy, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), |
182 | SLOT(checkPersistent())); |
183 | } |
184 | |
185 | public slots: |
186 | |
187 | void storePersistent(const QModelIndex &parent) |
188 | { |
189 | for (int row = 0; row < m_proxy->rowCount(parent); ++row) { |
190 | QModelIndex proxyIndex = m_proxy->index(row, column: 0, parent); |
191 | QModelIndex sourceIndex = m_proxy->mapToSource(proxyIndex); |
192 | if (!proxyIndex.isValid()) { |
193 | qWarning(msg: "%s: Invalid proxy index" , Q_FUNC_INFO); |
194 | ++storePersistentFailureCount; |
195 | } |
196 | if (!sourceIndex.isValid()) { |
197 | qWarning(msg: "%s: invalid source index" , Q_FUNC_INFO); |
198 | ++storePersistentFailureCount; |
199 | } |
200 | m_persistentSourceIndexes.append(t: sourceIndex); |
201 | m_persistentProxyIndexes.append(t: proxyIndex); |
202 | if (m_proxy->hasChildren(parent: proxyIndex)) |
203 | storePersistent(parent: proxyIndex); |
204 | } |
205 | } |
206 | |
207 | void storePersistent() |
208 | { |
209 | // This method is called from rowsAboutToBeMoved. Persistent indexes should be valid |
210 | foreach (const QModelIndex &idx, m_persistentProxyIndexes) |
211 | if (!idx.isValid()) { |
212 | qWarning(msg: "%s: persistentProxyIndexes contains invalid index" , Q_FUNC_INFO); |
213 | ++storePersistentFailureCount; |
214 | } |
215 | |
216 | if (!m_proxy->persistent().isEmpty()) { |
217 | qWarning(msg: "%s: proxy should have no persistent indexes when storePersistent called" , |
218 | Q_FUNC_INFO); |
219 | ++storePersistentFailureCount; |
220 | } |
221 | storePersistent(parent: QModelIndex()); |
222 | if (m_proxy->persistent().isEmpty()) { |
223 | qWarning(msg: "%s: proxy should have persistent index after storePersistent called" , |
224 | Q_FUNC_INFO); |
225 | ++storePersistentFailureCount; |
226 | } |
227 | } |
228 | |
229 | void checkPersistent() |
230 | { |
231 | for (int row = 0; row < m_persistentProxyIndexes.size(); ++row) { |
232 | m_persistentProxyIndexes.at(i: row); |
233 | m_persistentSourceIndexes.at(i: row); |
234 | } |
235 | for (int row = 0; row < m_persistentProxyIndexes.size(); ++row) { |
236 | QModelIndex updatedProxy = m_persistentProxyIndexes.at(i: row); |
237 | QModelIndex updatedSource = m_persistentSourceIndexes.at(i: row); |
238 | if (m_proxy->mapToSource(proxyIndex: updatedProxy) != updatedSource) { |
239 | qWarning(msg: "%s: check failed at row %d" , Q_FUNC_INFO, row); |
240 | ++checkPersistentFailureCount; |
241 | } |
242 | } |
243 | m_persistentSourceIndexes.clear(); |
244 | m_persistentProxyIndexes.clear(); |
245 | } |
246 | |
247 | private: |
248 | AccessibleProxyModel *m_proxy; |
249 | QList<QPersistentModelIndex> m_persistentSourceIndexes; |
250 | QList<QPersistentModelIndex> m_persistentProxyIndexes; |
251 | public: |
252 | int storePersistentFailureCount; |
253 | int checkPersistentFailureCount; |
254 | }; |
255 | |
256 | void tst_QAbstractItemModelTester::moveSourceItems() |
257 | { |
258 | DynamicTreeModel *model = new DynamicTreeModel(this); |
259 | AccessibleProxyModel *proxy = new AccessibleProxyModel(this); |
260 | proxy->setSourceModel(model); |
261 | |
262 | ModelInsertCommand *insertCommand = new ModelInsertCommand(model, this); |
263 | insertCommand->setStartRow(0); |
264 | insertCommand->setEndRow(2); |
265 | insertCommand->doCommand(); |
266 | |
267 | insertCommand = new ModelInsertCommand(model, this); |
268 | insertCommand->setAncestorRowNumbers(QList<int>() << 1); |
269 | insertCommand->setStartRow(0); |
270 | insertCommand->setEndRow(2); |
271 | insertCommand->doCommand(); |
272 | |
273 | ObservingObject observer(proxy); |
274 | |
275 | ModelMoveCommand *moveCommand = new ModelMoveCommand(model, this); |
276 | moveCommand->setStartRow(0); |
277 | moveCommand->setEndRow(0); |
278 | moveCommand->setDestAncestors(QList<int>() << 1); |
279 | moveCommand->setDestRow(0); |
280 | moveCommand->doCommand(); |
281 | |
282 | QCOMPARE(observer.storePersistentFailureCount, 0); |
283 | QCOMPARE(observer.checkPersistentFailureCount, 0); |
284 | } |
285 | |
286 | void tst_QAbstractItemModelTester::testResetThroughProxy() |
287 | { |
288 | DynamicTreeModel *model = new DynamicTreeModel(this); |
289 | |
290 | ModelInsertCommand *insertCommand = new ModelInsertCommand(model, this); |
291 | insertCommand->setStartRow(0); |
292 | insertCommand->setEndRow(2); |
293 | insertCommand->doCommand(); |
294 | |
295 | QPersistentModelIndex persistent = model->index(row: 0, column: 0); |
296 | |
297 | AccessibleProxyModel *proxy = new AccessibleProxyModel(this); |
298 | proxy->setSourceModel(model); |
299 | |
300 | ObservingObject observer(proxy); |
301 | observer.storePersistent(); |
302 | |
303 | ModelResetCommand *resetCommand = new ModelResetCommand(model, this); |
304 | resetCommand->setNumCols(0); |
305 | resetCommand->doCommand(); |
306 | |
307 | QCOMPARE(observer.storePersistentFailureCount, 0); |
308 | QCOMPARE(observer.checkPersistentFailureCount, 0); |
309 | } |
310 | |
311 | QTEST_MAIN(tst_QAbstractItemModelTester) |
312 | #include "tst_qabstractitemmodeltester.moc" |
313 | |