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 <qtest.h> |
30 | #include <QQmlEngine> |
31 | #include <QQmlComponent> |
32 | #include <QDebug> |
33 | #include <QStringListModel> |
34 | #include "../../shared/util.h" |
35 | #include "testtypes.h" |
36 | #include "qtestmodel.h" |
37 | |
38 | #define INIT_TEST_OBJECT(fileName, object) \ |
39 | QQmlComponent component_##object(&engine, testFileUrl(fileName)); \ |
40 | QScopedPointer<ItemModelsTest>object(qobject_cast<ItemModelsTest *>(component_##object.create())); \ |
41 | |
42 | |
43 | class tst_qqmlitemmodels : public QQmlDataTest |
44 | { |
45 | Q_OBJECT |
46 | |
47 | public: |
48 | tst_qqmlitemmodels() {} |
49 | |
50 | private slots: |
51 | void initTestCase(); |
52 | |
53 | void modelIndex(); |
54 | void persistentModelIndex(); |
55 | void modelIndexConversion(); |
56 | void itemSelectionRange(); |
57 | void itemSelection(); |
58 | void modelIndexList(); |
59 | |
60 | private: |
61 | QQmlEngine engine; |
62 | }; |
63 | |
64 | void tst_qqmlitemmodels::initTestCase() |
65 | { |
66 | QQmlDataTest::initTestCase(); |
67 | qmlRegisterType<ItemModelsTest>(uri: "Test" , versionMajor: 1, versionMinor: 0, qmlName: "ItemModelsTest" ); |
68 | } |
69 | |
70 | void tst_qqmlitemmodels::modelIndex() |
71 | { |
72 | INIT_TEST_OBJECT("modelindex.qml" , object); |
73 | TestModel model(10, 10); |
74 | |
75 | QModelIndex index = object->modelIndex(); |
76 | for (int i = 0; i < 5; i++) { |
77 | QCOMPARE(object->property("isValid" ).toBool(), index.isValid()); |
78 | QCOMPARE(object->property("row" ).toInt(), index.row()); |
79 | QCOMPARE(object->property("column" ).toInt(), index.column()); |
80 | QCOMPARE(object->property("parent" ).toModelIndex(), index.parent()); |
81 | QCOMPARE(object->property("model" ).value<QAbstractItemModel *>(), index.model()); |
82 | QCOMPARE(object->property("internalId" ).toULongLong(), index.internalId()); |
83 | |
84 | if (i < 3) { |
85 | index = model.index(row: 2 + i, column: 4 - i, parent: index); |
86 | object->setModelIndex(index); |
87 | } else if (i < 4) { |
88 | index = model.index(row: 2 + i, column: 4 - i); |
89 | object->emitSignalWithModelIndex(index); |
90 | } |
91 | } |
92 | } |
93 | |
94 | void tst_qqmlitemmodels::persistentModelIndex() |
95 | { |
96 | INIT_TEST_OBJECT("persistentmodelindex.qml" , object); |
97 | TestModel model(10, 10); |
98 | |
99 | QPersistentModelIndex index = object->persistentModelIndex(); |
100 | for (int i = 0; i < 5; i++) { |
101 | QCOMPARE(object->property("isValid" ).toBool(), index.isValid()); |
102 | QCOMPARE(object->property("row" ).toInt(), index.row()); |
103 | QCOMPARE(object->property("column" ).toInt(), index.column()); |
104 | QCOMPARE(object->property("parent" ).toModelIndex(), index.parent()); |
105 | QCOMPARE(object->property("model" ).value<QAbstractItemModel *>(), index.model()); |
106 | QCOMPARE(object->property("internalId" ).toULongLong(), index.internalId()); |
107 | |
108 | if (i < 2) { |
109 | index = model.index(row: 2 + i, column: 4 - i, parent: index); |
110 | object->setPersistentModelIndex(index); |
111 | } else if (i < 3) { |
112 | model.removeRow(arow: 2); |
113 | QVERIFY(!index.isValid()); // QPersistentModelIndex should update |
114 | object->emitChanged(); // Help QML get the new values as QPMI doesn't emit anything |
115 | } else if (i < 4) { |
116 | index = model.index(row: 2 + i, column: 4 - i); |
117 | object->emitSignalWithPersistentModelIndex(index); |
118 | } |
119 | } |
120 | |
121 | const QVariant &pmiVariant = object->property(name: "pmi" ); |
122 | QCOMPARE(pmiVariant.userType(), qMetaTypeId<QPersistentModelIndex>()); |
123 | QCOMPARE(pmiVariant.value<QPersistentModelIndex>(), QPersistentModelIndex(model.index(0, 0))); |
124 | } |
125 | |
126 | void tst_qqmlitemmodels::itemSelectionRange() |
127 | { |
128 | INIT_TEST_OBJECT("itemselectionrange.qml" , object); |
129 | TestModel model(10, 10); |
130 | |
131 | for (int i = 0; i < 2; i++) { |
132 | const QVariant &isrVariant = object->property(name: "itemSelectionRange" ); |
133 | QCOMPARE(isrVariant.userType(), qMetaTypeId<QItemSelectionRange>()); |
134 | const QItemSelectionRange &isr = isrVariant.value<QItemSelectionRange>(); |
135 | if (i > 0) { |
136 | QModelIndex parentIndex = model.index(row: 0, column: 0); |
137 | QCOMPARE(QModelIndex(isr.topLeft()), model.index(3, 0, parentIndex)); |
138 | QCOMPARE(QModelIndex(isr.bottomRight()), model.index(5, 6, parentIndex)); |
139 | } else { |
140 | QCOMPARE(QModelIndex(isr.topLeft()), QModelIndex()); |
141 | QCOMPARE(QModelIndex(isr.bottomRight()), QModelIndex()); |
142 | } |
143 | |
144 | QCOMPARE(object->property("top" ).toInt(), isr.top()); |
145 | QCOMPARE(object->property("left" ).toInt(), isr.left()); |
146 | QCOMPARE(object->property("bottom" ).toInt(), isr.bottom()); |
147 | QCOMPARE(object->property("right" ).toInt(), isr.right()); |
148 | QCOMPARE(object->property("width" ).toInt(), isr.width()); |
149 | QCOMPARE(object->property("height" ).toInt(), isr.height()); |
150 | QCOMPARE(object->property("isValid" ).toBool(), isr.isValid()); |
151 | QCOMPARE(object->property("isEmpty" ).toBool(), isr.isEmpty()); |
152 | QCOMPARE(object->property("isrModel" ).value<QAbstractItemModel *>(), isr.model()); |
153 | |
154 | // Set model for the 2nd iteration and test again |
155 | object->setModel(&model); |
156 | } |
157 | |
158 | // Check API function calls |
159 | QVERIFY(object->property("contains1" ).toBool()); |
160 | QVERIFY(object->property("contains2" ).toBool()); |
161 | QVERIFY(!object->property("intersects" ).toBool()); |
162 | const QVariant &isrVariant = object->property(name: "intersected" ); |
163 | QCOMPARE(isrVariant.userType(), qMetaTypeId<QItemSelectionRange>()); |
164 | } |
165 | |
166 | void tst_qqmlitemmodels::modelIndexConversion() |
167 | { |
168 | INIT_TEST_OBJECT("modelindexconversion.qml" , object); |
169 | TestModel model(10, 10); |
170 | object->setModel(&model); |
171 | |
172 | QCOMPARE(object->modelIndex(), model.index(0, 0)); |
173 | QCOMPARE(object->persistentModelIndex(), QPersistentModelIndex(model.index(1, 1))); |
174 | } |
175 | |
176 | void tst_qqmlitemmodels::itemSelection() |
177 | { |
178 | INIT_TEST_OBJECT("itemselection.qml" , object); |
179 | TestModel model(10, 10); |
180 | |
181 | object->setModel(&model); |
182 | QCOMPARE(object->property("count" ).toInt(), 5); |
183 | QCOMPARE(object->property("contains" ).toBool(), true); |
184 | |
185 | const char *propNames[] = { "itemSelectionRead" , "itemSelectionBinding" , nullptr }; |
186 | for (const char **name = propNames; *name; name++) { |
187 | QVariant isVariant = object->property(name: *name); |
188 | QCOMPARE(isVariant.userType(), qMetaTypeId<QItemSelection>()); |
189 | |
190 | const QItemSelection &sel = isVariant.value<QItemSelection>(); |
191 | QCOMPARE(sel.count(), object->itemSelection().count()); |
192 | QCOMPARE(sel, object->itemSelection()); |
193 | } |
194 | } |
195 | |
196 | void tst_qqmlitemmodels::modelIndexList() |
197 | { |
198 | INIT_TEST_OBJECT("modelindexlist.qml" , object); |
199 | TestModel model(10, 10); |
200 | model.fetchMore(QModelIndex()); |
201 | |
202 | object->setModel(&model); |
203 | QVERIFY(object->property("propIsArray" ).toBool()); |
204 | QVERIFY(object->property("varPropIsArray" ).toBool()); |
205 | QVERIFY(object->property("varIsArray" ).toBool()); |
206 | |
207 | QCOMPARE(object->property("count" ).toInt(), 10); |
208 | const QModelIndexList &mil = object->modelIndexList(); |
209 | QCOMPARE(mil.count(), 4); |
210 | for (int i = 0; i < 3; i++) |
211 | QCOMPARE(mil.at(i), model.index(2 + i, 2 + i)); |
212 | QCOMPARE(mil.at(3), QModelIndex()); // The string inserted at the end should result in an invalid index |
213 | QCOMPARE(mil.at(0), object->modelIndex()); |
214 | |
215 | QVariant cppMILVariant = object->property(name: "modelIndexListCopy" ); |
216 | QCOMPARE(cppMILVariant.userType(), qMetaTypeId<QModelIndexList>()); |
217 | QModelIndexList someMIL = object->someModelIndexList(); |
218 | QCOMPARE(cppMILVariant.value<QModelIndexList>(), someMIL); |
219 | |
220 | const char *propNames[] = { "modelIndexListRead" , "modelIndexListBinding" , nullptr }; |
221 | for (const char **name = propNames; *name; name++) { |
222 | QVariant milVariant = object->property(name: *name); |
223 | QCOMPARE(milVariant.userType(), qMetaTypeId<QModelIndexList>()); |
224 | |
225 | const QModelIndexList &milProp = milVariant.value<QModelIndexList>(); |
226 | QCOMPARE(milProp.count(), mil.count()); |
227 | QCOMPARE(milProp, mil); |
228 | } |
229 | } |
230 | |
231 | #undef INIT_TEST_OBJECT |
232 | |
233 | QTEST_MAIN(tst_qqmlitemmodels) |
234 | |
235 | #include "tst_qqmlitemmodels.moc" |
236 | |