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 "viewtestutil.h"
30
31#include <QtCore/QRandomGenerator>
32#include <QtQuick/QQuickView>
33#include <QtQuick/QQuickView>
34#include <QtGui/QScreen>
35
36#include <QtTest/QTest>
37
38#include <private/qquickwindow_p.h>
39#include <private/qquickitemview_p_p.h>
40
41QT_BEGIN_NAMESPACE
42
43QQuickView *QQuickViewTestUtil::createView()
44{
45 QQuickView *window = new QQuickView(0);
46 const QSize size(240, 320);
47 window->resize(newSize: size);
48 QQuickViewTestUtil::centerOnScreen(window, size);
49 return window;
50}
51
52void QQuickViewTestUtil::centerOnScreen(QQuickView *window, const QSize &size)
53{
54 const QRect screenGeometry = window->screen()->availableGeometry();
55 const QPoint offset = QPoint(size.width() / 2, size.height() / 2);
56 window->setFramePosition(screenGeometry.center() - offset);
57}
58
59void QQuickViewTestUtil::centerOnScreen(QQuickView *window)
60{
61 QQuickViewTestUtil::centerOnScreen(window, size: window->size());
62}
63
64void QQuickViewTestUtil::moveMouseAway(QQuickView *window)
65{
66#if QT_CONFIG(cursor) // Get the cursor out of the way.
67 QCursor::setPos(window->geometry().topRight() + QPoint(100, 100));
68#else
69 Q_UNUSED(window)
70#endif
71}
72
73void QQuickViewTestUtil::moveAndRelease(QQuickView *window, const QPoint &position)
74{
75 QTest::mouseMove(window, pos: position);
76 QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: {}, pos: position);
77}
78
79void QQuickViewTestUtil::moveAndPress(QQuickView *window, const QPoint &position)
80{
81 QTest::mouseMove(window, pos: position);
82 QTest::mousePress(window, button: Qt::LeftButton, stateKey: {}, pos: position);
83}
84
85void QQuickViewTestUtil::flick(QQuickView *window, const QPoint &from, const QPoint &to, int duration)
86{
87 const int pointCount = 5;
88 QPoint diff = to - from;
89
90 // send press, five equally spaced moves, and release.
91 moveAndPress(window, position: from);
92
93 for (int i = 0; i < pointCount; ++i)
94 QTest::mouseMove(window, pos: from + (i+1)*diff/pointCount, delay: duration / pointCount);
95
96 moveAndRelease(window, position: to);
97 QTest::qWait(ms: 50);
98}
99
100QList<int> QQuickViewTestUtil::adjustIndexesForAddDisplaced(const QList<int> &indexes, int index, int count)
101{
102 QList<int> result;
103 for (int i=0; i<indexes.count(); i++) {
104 int num = indexes[i];
105 if (num >= index) {
106 num += count;
107 }
108 result << num;
109 }
110 return result;
111}
112
113QList<int> QQuickViewTestUtil::adjustIndexesForMove(const QList<int> &indexes, int from, int to, int count)
114{
115 QList<int> result;
116 for (int i=0; i<indexes.count(); i++) {
117 int num = indexes[i];
118 if (from < to) {
119 if (num >= from && num < from + count)
120 num += (to - from); // target
121 else if (num >= from && num < to + count)
122 num -= count; // displaced
123 } else if (from > to) {
124 if (num >= from && num < from + count)
125 num -= (from - to); // target
126 else if (num >= to && num < from + count)
127 num += count; // displaced
128 }
129 result << num;
130 }
131 return result;
132}
133
134QList<int> QQuickViewTestUtil::adjustIndexesForRemoveDisplaced(const QList<int> &indexes, int index, int count)
135{
136 QList<int> result;
137 for (int i=0; i<indexes.count(); i++) {
138 int num = indexes[i];
139 if (num >= index)
140 num -= count;
141 result << num;
142 }
143 return result;
144}
145
146QQuickViewTestUtil::QaimModel::QaimModel(QObject *parent)
147 : QAbstractListModel(parent)
148{
149}
150
151int QQuickViewTestUtil::QaimModel::rowCount(const QModelIndex &parent) const
152{
153 Q_UNUSED(parent);
154 return list.count();
155}
156
157int QQuickViewTestUtil::QaimModel::columnCount(const QModelIndex &parent) const
158{
159 Q_UNUSED(parent);
160 return columns;
161}
162
163QHash<int,QByteArray> QQuickViewTestUtil::QaimModel::roleNames() const
164{
165 QHash<int,QByteArray> roles = QAbstractListModel::roleNames();
166 roles.insert(akey: Name, avalue: "name");
167 roles.insert(akey: Number, avalue: "number");
168 return roles;
169}
170
171QVariant QQuickViewTestUtil::QaimModel::data(const QModelIndex &index, int role) const
172{
173 QVariant rv;
174 if (role == Name)
175 rv = list.at(i: index.row()).first;
176 else if (role == Number)
177 rv = list.at(i: index.row()).second;
178
179 return rv;
180}
181
182int QQuickViewTestUtil::QaimModel::count() const
183{
184 return rowCount() * columnCount();
185}
186
187QString QQuickViewTestUtil::QaimModel::name(int index) const
188{
189 return list.at(i: index).first;
190}
191
192QString QQuickViewTestUtil::QaimModel::number(int index) const
193{
194 return list.at(i: index).second;
195}
196
197void QQuickViewTestUtil::QaimModel::addItem(const QString &name, const QString &number)
198{
199 emit beginInsertRows(parent: QModelIndex(), first: list.count(), last: list.count());
200 list.append(t: QPair<QString,QString>(name, number));
201 emit endInsertRows();
202}
203
204void QQuickViewTestUtil::QaimModel::addItems(const QList<QPair<QString, QString> > &items)
205{
206 emit beginInsertRows(parent: QModelIndex(), first: list.count(), last: list.count()+items.count()-1);
207 for (int i=0; i<items.count(); i++)
208 list.append(t: QPair<QString,QString>(items[i].first, items[i].second));
209 emit endInsertRows();
210}
211
212void QQuickViewTestUtil::QaimModel::insertItem(int index, const QString &name, const QString &number)
213{
214 emit beginInsertRows(parent: QModelIndex(), first: index, last: index);
215 list.insert(i: index, t: QPair<QString,QString>(name, number));
216 emit endInsertRows();
217}
218
219void QQuickViewTestUtil::QaimModel::insertItems(int index, const QList<QPair<QString, QString> > &items)
220{
221 emit beginInsertRows(parent: QModelIndex(), first: index, last: index+items.count()-1);
222 for (int i=0; i<items.count(); i++)
223 list.insert(i: index + i, t: QPair<QString,QString>(items[i].first, items[i].second));
224 emit endInsertRows();
225}
226
227void QQuickViewTestUtil::QaimModel::removeItem(int index)
228{
229 emit beginRemoveRows(parent: QModelIndex(), first: index, last: index);
230 list.removeAt(i: index);
231 emit endRemoveRows();
232}
233
234void QQuickViewTestUtil::QaimModel::removeItems(int index, int count)
235{
236 emit beginRemoveRows(parent: QModelIndex(), first: index, last: index+count-1);
237 while (count--)
238 list.removeAt(i: index);
239 emit endRemoveRows();
240}
241
242void QQuickViewTestUtil::QaimModel::moveItem(int from, int to)
243{
244 emit beginMoveRows(sourceParent: QModelIndex(), sourceFirst: from, sourceLast: from, destinationParent: QModelIndex(), destinationRow: to);
245 list.move(from, to);
246 emit endMoveRows();
247}
248
249void QQuickViewTestUtil::QaimModel::moveItems(int from, int to, int count)
250{
251 emit beginMoveRows(sourceParent: QModelIndex(), sourceFirst: from, sourceLast: from+count-1, destinationParent: QModelIndex(), destinationRow: to > from ? to+count : to);
252 qquickmodelviewstestutil_move(from, to, n: count, items: &list);
253 emit endMoveRows();
254}
255
256void QQuickViewTestUtil::QaimModel::modifyItem(int idx, const QString &name, const QString &number)
257{
258 list[idx] = QPair<QString,QString>(name, number);
259 emit dataChanged(topLeft: index(row: idx,column: 0), bottomRight: index(row: idx,column: 0));
260}
261
262void QQuickViewTestUtil::QaimModel::clear()
263{
264 int count = list.count();
265 if (count > 0) {
266 beginRemoveRows(parent: QModelIndex(), first: 0, last: count-1);
267 list.clear();
268 endRemoveRows();
269 }
270}
271
272void QQuickViewTestUtil::QaimModel::reset()
273{
274 emit beginResetModel();
275 emit endResetModel();
276}
277
278void QQuickViewTestUtil::QaimModel::resetItems(const QList<QPair<QString, QString> > &items)
279{
280 beginResetModel();
281 list = items;
282 endResetModel();
283}
284
285class ScopedPrintable
286{
287 Q_DISABLE_COPY_MOVE(ScopedPrintable)
288
289public:
290 ScopedPrintable(const QString &string) : data(QTest::toString(str: string)) {}
291 ~ScopedPrintable() { delete[] data; }
292
293 operator const char*() const { return data; }
294
295private:
296 const char *data;
297};
298
299void QQuickViewTestUtil::QaimModel::matchAgainst(const QList<QPair<QString, QString> > &other, const QString &error1, const QString &error2) {
300 for (int i=0; i<other.count(); i++) {
301 QVERIFY2(list.contains(other[i]),
302 ScopedPrintable(other[i].first + QLatin1Char(' ') + other[i].second + QLatin1Char(' ') + error1));
303 }
304 for (int i=0; i<list.count(); i++) {
305 QVERIFY2(other.contains(list[i]),
306 ScopedPrintable(list[i].first + QLatin1Char(' ') + list[i].second + QLatin1Char(' ') + error2));
307 }
308}
309
310
311
312QQuickViewTestUtil::ListRange::ListRange()
313 : valid(false)
314{
315}
316
317QQuickViewTestUtil::ListRange::ListRange(const ListRange &other)
318 : valid(other.valid)
319{
320 indexes = other.indexes;
321}
322
323QQuickViewTestUtil::ListRange::ListRange(int start, int end)
324 : valid(true)
325{
326 for (int i=start; i<=end; i++)
327 indexes << i;
328}
329
330QQuickViewTestUtil::ListRange::~ListRange()
331{
332}
333
334QQuickViewTestUtil::ListRange QQuickViewTestUtil::ListRange::operator+(const ListRange &other) const
335{
336 if (other == *this)
337 return *this;
338 ListRange a(*this);
339 a.indexes.append(t: other.indexes);
340 return a;
341}
342
343bool QQuickViewTestUtil::ListRange::operator==(const ListRange &other) const
344{
345 return QSet<int>(indexes.cbegin(), indexes.cend())
346 == QSet<int>(other.indexes.cbegin(), other.indexes.cend());
347}
348
349bool QQuickViewTestUtil::ListRange::operator!=(const ListRange &other) const
350{
351 return !(*this == other);
352}
353
354bool QQuickViewTestUtil::ListRange::isValid() const
355{
356 return valid;
357}
358
359int QQuickViewTestUtil::ListRange::count() const
360{
361 return indexes.count();
362}
363
364QList<QPair<QString,QString> > QQuickViewTestUtil::ListRange::getModelDataValues(const QaimModel &model)
365{
366 QList<QPair<QString,QString> > data;
367 if (!valid)
368 return data;
369 for (int i=0; i<indexes.count(); i++)
370 data.append(t: qMakePair(x: model.name(index: indexes[i]), y: model.number(index: indexes[i])));
371 return data;
372}
373
374QQuickViewTestUtil::StressTestModel::StressTestModel()
375 : QAbstractListModel()
376 , m_rowCount(20)
377{
378 QTimer *t = new QTimer(this);
379 t->setInterval(500);
380 t->start();
381
382 connect(sender: t, signal: &QTimer::timeout, receiver: this, slot: &StressTestModel::updateModel);
383}
384
385int QQuickViewTestUtil::StressTestModel::rowCount(const QModelIndex &) const
386{
387 return m_rowCount;
388}
389
390QVariant QQuickViewTestUtil::StressTestModel::data(const QModelIndex &, int) const
391{
392 return QVariant();
393}
394
395void QQuickViewTestUtil::StressTestModel::updateModel()
396{
397 if (m_rowCount > 10) {
398 for (int i = 0; i < 10; ++i) {
399 int rnum = QRandomGenerator::global()->bounded(highest: m_rowCount);
400 beginRemoveRows(parent: QModelIndex(), first: rnum, last: rnum);
401 m_rowCount--;
402 endRemoveRows();
403 }
404 }
405 if (m_rowCount < 20) {
406 for (int i = 0; i < 10; ++i) {
407 int rnum = QRandomGenerator::global()->bounded(highest: m_rowCount);
408 beginInsertRows(parent: QModelIndex(), first: rnum, last: rnum);
409 m_rowCount++;
410 endInsertRows();
411 }
412 }
413}
414
415bool QQuickViewTestUtil::testVisibleItems(const QQuickItemViewPrivate *priv, bool *nonUnique, FxViewItem **failItem, int *expectedIdx)
416{
417 QHash<QQuickItem*, int> uniqueItems;
418
419 int skip = 0;
420 for (int i = 0; i < priv->visibleItems.count(); ++i) {
421 FxViewItem *item = priv->visibleItems.at(i);
422 if (!item) {
423 *failItem = nullptr;
424 return false;
425 }
426#if 0
427 qDebug() << "\t" << item->index
428 << item->item
429 << item->position()
430 << (!item->item || QQuickItemPrivate::get(item->item)->culled ? "hidden" : "visible");
431#endif
432 if (item->index == -1) {
433 ++skip;
434 } else if (item->index != priv->visibleIndex + i - skip) {
435 *nonUnique = false;
436 *failItem = item;
437 *expectedIdx = priv->visibleIndex + i - skip;
438 return false;
439 } else if (uniqueItems.contains(akey: item->item)) {
440 *nonUnique = true;
441 *failItem = item;
442 *expectedIdx = uniqueItems.find(akey: item->item).value();
443 return false;
444 }
445
446 uniqueItems.insert(akey: item->item, avalue: item->index);
447 }
448
449 return true;
450}
451
452namespace QQuickTouchUtils {
453
454 /* QQuickWindow does event compression and only delivers events just
455 * before it is about to render the next frame. Since some tests
456 * rely on events being delivered immediately AND that no other
457 * event processing has occurred in the meanwhile, we flush the
458 * event manually and immediately.
459 */
460 void flush(QQuickWindow *window) {
461 if (!window)
462 return;
463 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(c: window);
464 if (!wd || !wd->delayedTouch)
465 return;
466 wd->deliverDelayedTouchEvent();
467 }
468
469}
470
471namespace QQuickTest {
472 // Initialize view, set Url, center in available geometry, move mouse away if desired
473 bool initView(QQuickView &v, const QUrl &url, bool moveMouseOut, QByteArray *errorMessage)
474 {
475 v.setBaseSize(QSize(240,320));
476 v.setSource(url);
477 while (v.status() == QQuickView::Loading)
478 QTest::qWait(ms: 10);
479 if (v.status() != QQuickView::Ready) {
480 foreach (const QQmlError &e, v.errors())
481 errorMessage->append(a: e.toString().toLocal8Bit() + '\n');
482 return false;
483 }
484 const QRect screenGeometry = v.screen()->availableGeometry();
485 const QSize size = v.size();
486 const QPoint offset = QPoint(size.width() / 2, size.height() / 2);
487 v.setFramePosition(screenGeometry.center() - offset);
488 #if QT_CONFIG(cursor) // Get the cursor out of the way.
489 if (moveMouseOut)
490 QCursor::setPos(v.geometry().topRight() + QPoint(100, 100));
491 #else
492 Q_UNUSED(moveMouseOut)
493 #endif
494 return true;
495 }
496}
497
498QT_END_NAMESPACE
499

source code of qtdeclarative/tests/auto/quick/shared/viewtestutil.cpp