/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "viewtestutil.h" #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE QQuickView *QQuickViewTestUtil::createView() { QQuickView *window = new QQuickView(0); const QSize size(240, 320); window->resize(size); QQuickViewTestUtil::centerOnScreen(window, size); return window; } void QQuickViewTestUtil::centerOnScreen(QQuickView *window, const QSize &size) { const QRect screenGeometry = window->screen()->availableGeometry(); const QPoint offset = QPoint(size.width() / 2, size.height() / 2); window->setFramePosition(screenGeometry.center() - offset); } void QQuickViewTestUtil::centerOnScreen(QQuickView *window) { QQuickViewTestUtil::centerOnScreen(window, window->size()); } void QQuickViewTestUtil::moveMouseAway(QQuickView *window) { #if QT_CONFIG(cursor) // Get the cursor out of the way. QCursor::setPos(window->geometry().topRight() + QPoint(100, 100)); #else Q_UNUSED(window); #endif } void QQuickViewTestUtil::moveAndRelease(QQuickView *window, const QPoint &position) { QTest::mouseMove(window, position); QTest::mouseRelease(window, Qt::LeftButton, {}, position); } void QQuickViewTestUtil::moveAndPress(QQuickView *window, const QPoint &position) { QTest::mouseMove(window, position); QTest::mousePress(window, Qt::LeftButton, {}, position); } void QQuickViewTestUtil::flick(QQuickView *window, const QPoint &from, const QPoint &to, int duration) { const int pointCount = 5; QPoint diff = to - from; // send press, five equally spaced moves, and release. moveAndPress(window, from); for (int i = 0; i < pointCount; ++i) QTest::mouseMove(window, from + (i+1)*diff/pointCount, duration / pointCount); moveAndRelease(window, to); QTest::qWait(50); } QList QQuickViewTestUtil::adjustIndexesForAddDisplaced(const QList &indexes, int index, int count) { QList result; for (int i=0; i= index) { num += count; } result << num; } return result; } QList QQuickViewTestUtil::adjustIndexesForMove(const QList &indexes, int from, int to, int count) { QList result; for (int i=0; i= from && num < from + count) num += (to - from); // target else if (num >= from && num < to + count) num -= count; // displaced } else if (from > to) { if (num >= from && num < from + count) num -= (from - to); // target else if (num >= to && num < from + count) num += count; // displaced } result << num; } return result; } QList QQuickViewTestUtil::adjustIndexesForRemoveDisplaced(const QList &indexes, int index, int count) { QList result; for (int i=0; i= index) num -= count; result << num; } return result; } QQuickViewTestUtil::QaimModel::QaimModel(QObject *parent) : QAbstractListModel(parent) { } int QQuickViewTestUtil::QaimModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); return list.count(); } int QQuickViewTestUtil::QaimModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return columns; } QHash QQuickViewTestUtil::QaimModel::roleNames() const { QHash roles = QAbstractListModel::roleNames(); roles.insert(Name, "name"); roles.insert(Number, "number"); return roles; } QVariant QQuickViewTestUtil::QaimModel::data(const QModelIndex &index, int role) const { QVariant rv; if (role == Name) rv = list.at(index.row()).first; else if (role == Number) rv = list.at(index.row()).second; return rv; } int QQuickViewTestUtil::QaimModel::count() const { return rowCount() * columnCount(); } QString QQuickViewTestUtil::QaimModel::name(int index) const { return list.at(index).first; } QString QQuickViewTestUtil::QaimModel::number(int index) const { return list.at(index).second; } void QQuickViewTestUtil::QaimModel::addItem(const QString &name, const QString &number) { emit beginInsertRows(QModelIndex(), list.count(), list.count()); list.append(QPair(name, number)); emit endInsertRows(); } void QQuickViewTestUtil::QaimModel::addItems(const QList > &items) { emit beginInsertRows(QModelIndex(), list.count(), list.count()+items.count()-1); for (int i=0; i(items[i].first, items[i].second)); emit endInsertRows(); } void QQuickViewTestUtil::QaimModel::insertItem(int index, const QString &name, const QString &number) { emit beginInsertRows(QModelIndex(), index, index); list.insert(index, QPair(name, number)); emit endInsertRows(); } void QQuickViewTestUtil::QaimModel::insertItems(int index, const QList > &items) { emit beginInsertRows(QModelIndex(), index, index+items.count()-1); for (int i=0; i(items[i].first, items[i].second)); emit endInsertRows(); } void QQuickViewTestUtil::QaimModel::removeItem(int index) { emit beginRemoveRows(QModelIndex(), index, index); list.removeAt(index); emit endRemoveRows(); } void QQuickViewTestUtil::QaimModel::removeItems(int index, int count) { emit beginRemoveRows(QModelIndex(), index, index+count-1); while (count--) list.removeAt(index); emit endRemoveRows(); } void QQuickViewTestUtil::QaimModel::moveItem(int from, int to) { emit beginMoveRows(QModelIndex(), from, from, QModelIndex(), to); list.move(from, to); emit endMoveRows(); } void QQuickViewTestUtil::QaimModel::moveItems(int from, int to, int count) { emit beginMoveRows(QModelIndex(), from, from+count-1, QModelIndex(), to > from ? to+count : to); qquickmodelviewstestutil_move(from, to, count, &list); emit endMoveRows(); } void QQuickViewTestUtil::QaimModel::modifyItem(int idx, const QString &name, const QString &number) { list[idx] = QPair(name, number); emit dataChanged(index(idx,0), index(idx,0)); } void QQuickViewTestUtil::QaimModel::clear() { int count = list.count(); if (count > 0) { beginRemoveRows(QModelIndex(), 0, count-1); list.clear(); endRemoveRows(); } } void QQuickViewTestUtil::QaimModel::reset() { emit beginResetModel(); emit endResetModel(); } void QQuickViewTestUtil::QaimModel::resetItems(const QList > &items) { beginResetModel(); list = items; endResetModel(); } class ScopedPrintable { Q_DISABLE_COPY_MOVE(ScopedPrintable) public: ScopedPrintable(const QString &string) : data(QTest::toString(string)) {} ~ScopedPrintable() { delete[] data; } operator const char*() const { return data; } private: const char *data; }; void QQuickViewTestUtil::QaimModel::matchAgainst(const QList > &other, const QString &error1, const QString &error2) { for (int i=0; i(indexes.cbegin(), indexes.cend()) == QSet(other.indexes.cbegin(), other.indexes.cend()); } bool QQuickViewTestUtil::ListRange::operator!=(const ListRange &other) const { return !(*this == other); } bool QQuickViewTestUtil::ListRange::isValid() const { return valid; } int QQuickViewTestUtil::ListRange::count() const { return indexes.count(); } QList > QQuickViewTestUtil::ListRange::getModelDataValues(const QaimModel &model) { QList > data; if (!valid) return data; for (int i=0; isetInterval(500); t->start(); connect(t, &QTimer::timeout, this, &StressTestModel::updateModel); } int QQuickViewTestUtil::StressTestModel::rowCount(const QModelIndex &) const { return m_rowCount; } QVariant QQuickViewTestUtil::StressTestModel::data(const QModelIndex &, int) const { return QVariant(); } void QQuickViewTestUtil::StressTestModel::updateModel() { if (m_rowCount > 10) { for (int i = 0; i < 10; ++i) { int rnum = QRandomGenerator::global()->bounded(m_rowCount); beginRemoveRows(QModelIndex(), rnum, rnum); m_rowCount--; endRemoveRows(); } } if (m_rowCount < 20) { for (int i = 0; i < 10; ++i) { int rnum = QRandomGenerator::global()->bounded(m_rowCount); beginInsertRows(QModelIndex(), rnum, rnum); m_rowCount++; endInsertRows(); } } } bool QQuickViewTestUtil::testVisibleItems(const QQuickItemViewPrivate *priv, bool *nonUnique, FxViewItem **failItem, int *expectedIdx) { QHash uniqueItems; int skip = 0; for (int i = 0; i < priv->visibleItems.count(); ++i) { FxViewItem *item = priv->visibleItems.at(i); if (!item) { *failItem = nullptr; return false; } #if 0 qDebug() << "\t" << item->index << item->item << item->position() << (!item->item || QQuickItemPrivate::get(item->item)->culled ? "hidden" : "visible"); #endif if (item->index == -1) { ++skip; } else if (item->index != priv->visibleIndex + i - skip) { *nonUnique = false; *failItem = item; *expectedIdx = priv->visibleIndex + i - skip; return false; } else if (uniqueItems.contains(item->item)) { *nonUnique = true; *failItem = item; *expectedIdx = uniqueItems.find(item->item).value(); return false; } uniqueItems.insert(item->item, item->index); } return true; } namespace QQuickTouchUtils { /* QQuickWindow does event compression and only delivers events just * before it is about to render the next frame. Since some tests * rely on events being delivered immediately AND that no other * event processing has occurred in the meanwhile, we flush the * event manually and immediately. */ void flush(QQuickWindow *window) { if (!window) return; QQuickDeliveryAgentPrivate *da = QQuickWindowPrivate::get(window)->deliveryAgentPrivate(); if (!da || !da->delayedTouch) return; da->deliverDelayedTouchEvent(); } } namespace QQuickTest { /*! \internal Initialize \a view, set \a url, center in available geometry, move mouse away if desired. If \a errorMessage is given, QQuickView::errors() will be concatenated into it; otherwise, the QWARN messages are generally enough to debug the test. Returns \c false if the view fails to load the QML. That should be fatal in most tests, so normally the return value should be checked with QVERIFY. */ bool initView(QQuickView &view, const QUrl &url, bool moveMouseOut, QByteArray *errorMessage) { view.setSource(url); while (view.status() == QQuickView::Loading) QTest::qWait(10); if (view.status() != QQuickView::Ready) { if (errorMessage) { for (const QQmlError &e : view.errors()) errorMessage->append(e.toString().toLocal8Bit() + '\n'); } return false; } const QRect screenGeometry = view.screen()->availableGeometry(); const QSize size = view.size(); const QPoint offset = QPoint(size.width() / 2, size.height() / 2); view.setFramePosition(screenGeometry.center() - offset); #if QT_CONFIG(cursor) // Get the cursor out of the way. if (moveMouseOut) QCursor::setPos(view.geometry().topRight() + QPoint(100, 100)); #else Q_UNUSED(moveMouseOut); #endif return true; } /*! \internal Initialize \a view, set \a url, center in available geometry, move mouse away, show the \a view, wait for it to be exposed, and verify that its rootObject is not null. Returns \c false if anything fails, which should be fatal in most tests. The usual way to call this function is \code QQuickView window; QVERIFY(QQuickTest::showView(window, testFileUrl("myitems.qml"))); \endcode */ bool showView(QQuickView &view, const QUrl &url) { if (!initView(view, url)) return false; view.show(); if (!QTest::qWaitForWindowExposed(&view)) return false; if (!view.rootObject()) return false; return true; } } QT_END_NAMESPACE