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 <qabstractproxymodel.h>
31#include <QItemSelection>
32#include <qstandarditemmodel.h>
33
34class tst_QAbstractProxyModel : public QObject
35{
36 Q_OBJECT
37
38private slots:
39 void qabstractproxymodel();
40 void data_data();
41 void data();
42 void flags_data();
43 void flags();
44 void headerData_data();
45 void headerData();
46 void itemData_data();
47 void itemData();
48 void mapFromSource_data();
49 void mapFromSource();
50 void mapSelectionFromSource_data();
51 void mapSelectionFromSource();
52 void mapSelectionToSource_data();
53 void mapSelectionToSource();
54 void mapToSource_data();
55 void mapToSource();
56 void revert();
57 void setSourceModel();
58 void submit_data();
59 void submit();
60 void testRoleNames();
61 void testSwappingRowsProxy();
62 void testDragAndDrop();
63};
64
65// Subclass that exposes the protected functions.
66class SubQAbstractProxyModel : public QAbstractProxyModel
67{
68public:
69 // QAbstractProxyModel::mapFromSource is a pure virtual function.
70 QModelIndex mapFromSource(QModelIndex const& sourceIndex) const
71 { Q_UNUSED(sourceIndex); return QModelIndex(); }
72
73 // QAbstractProxyModel::mapToSource is a pure virtual function.
74 QModelIndex mapToSource(QModelIndex const& proxyIndex) const
75 { Q_UNUSED(proxyIndex); return QModelIndex(); }
76
77 QModelIndex index(int, int, const QModelIndex&) const
78 {
79 return QModelIndex();
80 }
81
82 QModelIndex parent(const QModelIndex&) const
83 {
84 return QModelIndex();
85 }
86
87 int rowCount(const QModelIndex&) const
88 {
89 return 0;
90 }
91
92 int columnCount(const QModelIndex&) const
93 {
94 return 0;
95 }
96};
97
98void tst_QAbstractProxyModel::qabstractproxymodel()
99{
100 SubQAbstractProxyModel model;
101 model.data(proxyIndex: QModelIndex());
102 model.flags(index: QModelIndex());
103 model.headerData(section: 0, orientation: Qt::Vertical, role: 0);
104 model.itemData(index: QModelIndex());
105 model.mapFromSource(sourceIndex: QModelIndex());
106 model.mapSelectionFromSource(selection: QItemSelection());
107 model.mapSelectionToSource(selection: QItemSelection());
108 model.mapToSource(proxyIndex: QModelIndex());
109 model.revert();
110 model.setSourceModel(0);
111 QCOMPARE(model.sourceModel(), (QAbstractItemModel*)0);
112 model.submit();
113}
114
115void tst_QAbstractProxyModel::data_data()
116{
117 QTest::addColumn<QModelIndex>(name: "proxyIndex");
118 QTest::addColumn<int>(name: "role");
119 QTest::addColumn<QVariant>(name: "data");
120 QTest::newRow(dataTag: "null") << QModelIndex() << 0 << QVariant();
121}
122
123// public QVariant data(QModelIndex const& proxyIndex, int role = Qt::DisplayRole) const
124void tst_QAbstractProxyModel::data()
125{
126 QFETCH(QModelIndex, proxyIndex);
127 QFETCH(int, role);
128 QFETCH(QVariant, data);
129
130 SubQAbstractProxyModel model;
131 QCOMPARE(model.data(proxyIndex, role), data);
132}
133
134Q_DECLARE_METATYPE(Qt::ItemFlags)
135void tst_QAbstractProxyModel::flags_data()
136{
137 QTest::addColumn<QModelIndex>(name: "index");
138 QTest::addColumn<Qt::ItemFlags>(name: "flags");
139 QTest::newRow(dataTag: "null") << QModelIndex() << Qt::ItemFlags{};
140}
141
142// public Qt::ItemFlags flags(QModelIndex const& index) const
143void tst_QAbstractProxyModel::flags()
144{
145 QFETCH(QModelIndex, index);
146 QFETCH(Qt::ItemFlags, flags);
147
148 SubQAbstractProxyModel model;
149 QCOMPARE(model.flags(index), flags);
150}
151
152Q_DECLARE_METATYPE(Qt::Orientation)
153Q_DECLARE_METATYPE(Qt::ItemDataRole)
154void tst_QAbstractProxyModel::headerData_data()
155{
156 QTest::addColumn<int>(name: "section");
157 QTest::addColumn<Qt::Orientation>(name: "orientation");
158 QTest::addColumn<Qt::ItemDataRole>(name: "role");
159 QTest::addColumn<QVariant>(name: "headerData");
160 QTest::newRow(dataTag: "null") << 0 << Qt::Vertical << Qt::UserRole << QVariant();
161}
162
163// public QVariant headerData(int section, Qt::Orientation orientation, int role) const
164void tst_QAbstractProxyModel::headerData()
165{
166 QFETCH(int, section);
167 QFETCH(Qt::Orientation, orientation);
168 QFETCH(Qt::ItemDataRole, role);
169 QFETCH(QVariant, headerData);
170
171 SubQAbstractProxyModel model;
172 QCOMPARE(model.headerData(section, orientation, role), headerData);
173}
174
175void tst_QAbstractProxyModel::itemData_data()
176{
177 QTest::addColumn<QModelIndex>(name: "index");
178 QTest::addColumn<int>(name: "count");
179
180 QTest::newRow(dataTag: "null") << QModelIndex() << 0;
181}
182
183// public QMap<int,QVariant> itemData(QModelIndex const& index) const
184void tst_QAbstractProxyModel::itemData()
185{
186 QFETCH(QModelIndex, index);
187 QFETCH(int, count);
188 SubQAbstractProxyModel model;
189 QCOMPARE(model.itemData(index).count(), count);
190}
191
192void tst_QAbstractProxyModel::mapFromSource_data()
193{
194 QTest::addColumn<QModelIndex>(name: "sourceIndex");
195 QTest::addColumn<QModelIndex>(name: "mapFromSource");
196 QTest::newRow(dataTag: "null") << QModelIndex() << QModelIndex();
197}
198
199// public QModelIndex mapFromSource(QModelIndex const& sourceIndex) const
200void tst_QAbstractProxyModel::mapFromSource()
201{
202 QFETCH(QModelIndex, sourceIndex);
203 QFETCH(QModelIndex, mapFromSource);
204
205 SubQAbstractProxyModel model;
206 QCOMPARE(model.mapFromSource(sourceIndex), mapFromSource);
207}
208
209void tst_QAbstractProxyModel::mapSelectionFromSource_data()
210{
211 QTest::addColumn<QItemSelection>(name: "selection");
212 QTest::addColumn<QItemSelection>(name: "mapSelectionFromSource");
213 QTest::newRow(dataTag: "null") << QItemSelection() << QItemSelection();
214 QTest::newRow(dataTag: "empty") << QItemSelection(QModelIndex(), QModelIndex()) << QItemSelection(QModelIndex(), QModelIndex());
215}
216
217// public QItemSelection mapSelectionFromSource(QItemSelection const& selection) const
218void tst_QAbstractProxyModel::mapSelectionFromSource()
219{
220 QFETCH(QItemSelection, selection);
221 QFETCH(QItemSelection, mapSelectionFromSource);
222
223 SubQAbstractProxyModel model;
224 QCOMPARE(model.mapSelectionFromSource(selection), mapSelectionFromSource);
225}
226
227void tst_QAbstractProxyModel::mapSelectionToSource_data()
228{
229 QTest::addColumn<QItemSelection>(name: "selection");
230 QTest::addColumn<QItemSelection>(name: "mapSelectionToSource");
231 QTest::newRow(dataTag: "null") << QItemSelection() << QItemSelection();
232 QTest::newRow(dataTag: "empty") << QItemSelection(QModelIndex(), QModelIndex()) << QItemSelection(QModelIndex(), QModelIndex());
233}
234
235// public QItemSelection mapSelectionToSource(QItemSelection const& selection) const
236void tst_QAbstractProxyModel::mapSelectionToSource()
237{
238 QFETCH(QItemSelection, selection);
239 QFETCH(QItemSelection, mapSelectionToSource);
240
241 SubQAbstractProxyModel model;
242 QCOMPARE(model.mapSelectionToSource(selection), mapSelectionToSource);
243}
244
245void tst_QAbstractProxyModel::mapToSource_data()
246{
247 QTest::addColumn<QModelIndex>(name: "proxyIndex");
248 QTest::addColumn<QModelIndex>(name: "mapToSource");
249 QTest::newRow(dataTag: "null") << QModelIndex() << QModelIndex();
250}
251
252// public QModelIndex mapToSource(QModelIndex const& proxyIndex) const
253void tst_QAbstractProxyModel::mapToSource()
254{
255 QFETCH(QModelIndex, proxyIndex);
256 QFETCH(QModelIndex, mapToSource);
257
258 SubQAbstractProxyModel model;
259 QCOMPARE(model.mapToSource(proxyIndex), mapToSource);
260}
261
262// public void revert()
263void tst_QAbstractProxyModel::revert()
264{
265 SubQAbstractProxyModel model;
266 model.revert();
267}
268
269// public void setSourceModel(QAbstractItemModel* sourceModel)
270void tst_QAbstractProxyModel::setSourceModel()
271{
272 SubQAbstractProxyModel model;
273
274 QCOMPARE(model.property("sourceModel"), QVariant::fromValue<QAbstractItemModel*>(0));
275 QStandardItemModel *sourceModel = new QStandardItemModel(&model);
276 model.setSourceModel(sourceModel);
277 QCOMPARE(model.sourceModel(), static_cast<QAbstractItemModel*>(sourceModel));
278
279 QCOMPARE(model.property("sourceModel").value<QObject*>(), static_cast<QObject*>(sourceModel));
280 QCOMPARE(model.property("sourceModel").value<QAbstractItemModel*>(), sourceModel);
281
282 QStandardItemModel *sourceModel2 = new QStandardItemModel(&model);
283 model.setSourceModel(sourceModel2);
284 QCOMPARE(model.sourceModel(), static_cast<QAbstractItemModel*>(sourceModel2));
285
286 QCOMPARE(model.property("sourceModel").value<QObject*>(), static_cast<QObject*>(sourceModel2));
287 QCOMPARE(model.property("sourceModel").value<QAbstractItemModel*>(), sourceModel2);
288
289 delete sourceModel2;
290 QCOMPARE(model.sourceModel(), static_cast<QAbstractItemModel*>(0));
291}
292
293void tst_QAbstractProxyModel::submit_data()
294{
295 QTest::addColumn<bool>(name: "submit");
296 QTest::newRow(dataTag: "null") << true;
297}
298
299// public bool submit()
300void tst_QAbstractProxyModel::submit()
301{
302 QFETCH(bool, submit);
303
304 SubQAbstractProxyModel model;
305 QCOMPARE(model.submit(), submit);
306}
307
308class StandardItemModelWithCustomRoleNames : public QStandardItemModel
309{
310public:
311 enum CustomRole {
312 CustomRole1 = Qt::UserRole,
313 CustomRole2
314 };
315
316 QHash<int, QByteArray> roleNames() const override
317 {
318 auto result = QStandardItemModel::roleNames();
319 result.insert(key: CustomRole1, QByteArrayLiteral("custom1"));
320 result.insert(key: CustomRole2, QByteArrayLiteral("custom2"));
321 return result;
322 }
323};
324
325class AnotherStandardItemModelWithCustomRoleNames : public QStandardItemModel
326{
327public:
328 enum CustomRole {
329 AnotherCustomRole1 = Qt::UserRole + 10, // Different to StandardItemModelWithCustomRoleNames::CustomRole1
330 AnotherCustomRole2
331 };
332
333 QHash<int, QByteArray> roleNames() const override
334 {
335 return {{AnotherCustomRole1, QByteArrayLiteral("another_custom1")},
336 {AnotherCustomRole2, QByteArrayLiteral("another_custom2")}};
337 }
338};
339
340/**
341 Verifies that @p subSet is a subset of @p superSet. That is, all keys in @p subSet exist in @p superSet and have the same values.
342*/
343static void verifySubSetOf(const QHash<int, QByteArray> &superSet, const QHash<int, QByteArray> &subSet)
344{
345 QHash<int, QByteArray>::const_iterator it = subSet.constBegin();
346 const QHash<int, QByteArray>::const_iterator end = subSet.constEnd();
347 for ( ; it != end; ++it ) {
348 QVERIFY(superSet.contains(it.key()));
349 QCOMPARE(it.value(), superSet.value(it.key()));
350 }
351}
352
353void tst_QAbstractProxyModel::testRoleNames()
354{
355 QStandardItemModel defaultModel;
356 StandardItemModelWithCustomRoleNames model;
357 QHash<int, QByteArray> rootModelRoleNames = model.roleNames();
358 QHash<int, QByteArray> defaultModelRoleNames = defaultModel.roleNames();
359
360 verifySubSetOf( superSet: rootModelRoleNames, subSet: defaultModelRoleNames);
361 QVERIFY( rootModelRoleNames.size() == defaultModelRoleNames.size() + 2 );
362 QVERIFY( rootModelRoleNames.contains(StandardItemModelWithCustomRoleNames::CustomRole1));
363 QVERIFY( rootModelRoleNames.contains(StandardItemModelWithCustomRoleNames::CustomRole2));
364 QVERIFY( rootModelRoleNames.value(StandardItemModelWithCustomRoleNames::CustomRole1) == "custom1" );
365 QVERIFY( rootModelRoleNames.value(StandardItemModelWithCustomRoleNames::CustomRole2) == "custom2" );
366
367 SubQAbstractProxyModel proxy1;
368 proxy1.setSourceModel(&model);
369 QHash<int, QByteArray> proxy1RoleNames = proxy1.roleNames();
370 verifySubSetOf( superSet: proxy1RoleNames, subSet: defaultModelRoleNames );
371 QVERIFY( proxy1RoleNames.size() == defaultModelRoleNames.size() + 2 );
372 QVERIFY( proxy1RoleNames.contains(StandardItemModelWithCustomRoleNames::CustomRole1));
373 QVERIFY( proxy1RoleNames.contains(StandardItemModelWithCustomRoleNames::CustomRole2));
374 QVERIFY( proxy1RoleNames.value(StandardItemModelWithCustomRoleNames::CustomRole1) == "custom1" );
375 QVERIFY( proxy1RoleNames.value(StandardItemModelWithCustomRoleNames::CustomRole2) == "custom2" );
376
377 SubQAbstractProxyModel proxy2;
378 proxy2.setSourceModel(&proxy1);
379 QHash<int, QByteArray> proxy2RoleNames = proxy2.roleNames();
380 verifySubSetOf( superSet: proxy2RoleNames, subSet: defaultModelRoleNames );
381 QVERIFY( proxy2RoleNames.size() == defaultModelRoleNames.size() + 2 );
382 QVERIFY( proxy2RoleNames.contains(StandardItemModelWithCustomRoleNames::CustomRole1));
383 QVERIFY( proxy2RoleNames.contains(StandardItemModelWithCustomRoleNames::CustomRole2));
384 QVERIFY( proxy2RoleNames.value(StandardItemModelWithCustomRoleNames::CustomRole1) == "custom1" );
385 QVERIFY( proxy2RoleNames.value(StandardItemModelWithCustomRoleNames::CustomRole2) == "custom2" );
386}
387
388// This class only supports very simple table models
389class SwappingProxy : public QAbstractProxyModel
390{
391 static int swapRow(const int row)
392 {
393 if (row == 2) {
394 return 3;
395 } else if (row == 3) {
396 return 2;
397 } else {
398 return row;
399 }
400 }
401public:
402 virtual QModelIndex index(int row, int column, const QModelIndex &parentIdx) const
403 {
404 if (!sourceModel())
405 return QModelIndex();
406 if (row < 0 || column < 0)
407 return QModelIndex();
408 if (row >= sourceModel()->rowCount())
409 return QModelIndex();
410 if (column >= sourceModel()->columnCount())
411 return QModelIndex();
412 return createIndex(arow: row, acolumn: column, adata: parentIdx.internalPointer());
413 }
414
415 virtual QModelIndex parent(const QModelIndex &parentIdx) const
416 {
417 // well, we're a 2D model
418 Q_UNUSED(parentIdx);
419 return QModelIndex();
420 }
421
422 virtual int rowCount(const QModelIndex &parentIdx) const
423 {
424 if (parentIdx.isValid() || !sourceModel())
425 return 0;
426 return sourceModel()->rowCount();
427 }
428
429 virtual int columnCount(const QModelIndex &parentIdx) const
430 {
431 if (parentIdx.isValid() || !sourceModel())
432 return 0;
433 return sourceModel()->rowCount();
434 }
435
436 virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const
437 {
438 if (!proxyIndex.isValid())
439 return QModelIndex();
440 if (!sourceModel())
441 return QModelIndex();
442 Q_ASSERT(!proxyIndex.parent().isValid());
443 return sourceModel()->index(row: swapRow(row: proxyIndex.row()), column: proxyIndex.column(), parent: QModelIndex());
444 }
445
446 virtual QModelIndex mapFromSource(const QModelIndex &sourceIndex) const
447 {
448 if (!sourceIndex.isValid())
449 return QModelIndex();
450 if (!sourceModel())
451 return QModelIndex();
452 Q_ASSERT(!sourceIndex.parent().isValid());
453 return index(row: swapRow(row: sourceIndex.row()), column: sourceIndex.column(), parentIdx: QModelIndex());
454 }
455};
456
457void tst_QAbstractProxyModel::testSwappingRowsProxy()
458{
459 QStandardItemModel defaultModel;
460 defaultModel.setRowCount(4);
461 defaultModel.setColumnCount(2);
462 for (int row = 0; row < defaultModel.rowCount(); ++row) {
463 defaultModel.setItem(row, column: 0, item: new QStandardItem(QString::number(row) + QLatin1Char('A')));
464 defaultModel.setItem(row, column: 1, item: new QStandardItem(QString::number(row) + QLatin1Char('B')));
465 }
466 SwappingProxy proxy;
467 proxy.setSourceModel(&defaultModel);
468 QCOMPARE(proxy.data(proxy.index(0, 0, QModelIndex())), QVariant("0A"));
469 QCOMPARE(proxy.data(proxy.index(0, 1, QModelIndex())), QVariant("0B"));
470 QCOMPARE(proxy.data(proxy.index(1, 0, QModelIndex())), QVariant("1A"));
471 QCOMPARE(proxy.data(proxy.index(1, 1, QModelIndex())), QVariant("1B"));
472 QCOMPARE(proxy.data(proxy.index(2, 0, QModelIndex())), QVariant("3A"));
473 QCOMPARE(proxy.data(proxy.index(2, 1, QModelIndex())), QVariant("3B"));
474 QCOMPARE(proxy.data(proxy.index(3, 0, QModelIndex())), QVariant("2A"));
475 QCOMPARE(proxy.data(proxy.index(3, 1, QModelIndex())), QVariant("2B"));
476
477 for (int row = 0; row < defaultModel.rowCount(); ++row) {
478 QModelIndex left = proxy.index(row, column: 0, parentIdx: QModelIndex());
479 QModelIndex right = proxy.index(row, column: 1, parentIdx: QModelIndex());
480 QCOMPARE(left.siblingAtColumn(1), right);
481 QCOMPARE(right.siblingAtColumn(0), left);
482 }
483}
484
485class StandardItemModelWithCustomDragAndDrop : public QStandardItemModel
486{
487public:
488 QStringList mimeTypes() const { return QStringList() << QStringLiteral("foo/mimetype"); }
489 Qt::DropActions supportedDragActions() const { return Qt::CopyAction | Qt::LinkAction; }
490 Qt::DropActions supportedDropActions() const { return Qt::MoveAction; }
491};
492
493void tst_QAbstractProxyModel::testDragAndDrop()
494{
495 StandardItemModelWithCustomDragAndDrop sourceModel;
496 SubQAbstractProxyModel proxy;
497 proxy.setSourceModel(&sourceModel);
498 QCOMPARE(proxy.mimeTypes(), sourceModel.mimeTypes());
499 QCOMPARE(proxy.supportedDragActions(), sourceModel.supportedDragActions());
500 QCOMPARE(proxy.supportedDropActions(), sourceModel.supportedDropActions());
501}
502
503
504QTEST_MAIN(tst_QAbstractProxyModel)
505#include "tst_qabstractproxymodel.moc"
506
507

source code of qtbase/tests/auto/corelib/itemmodels/qabstractproxymodel/tst_qabstractproxymodel.cpp