From d2305265af0552f9fcab4c93bed6b93e25a28f78 Mon Sep 17 00:00:00 2001 From: Gabriel de Dietrich Date: Mon, 23 Nov 2015 18:35:02 -0800 Subject: Auto-tests for fast model updates while flicking Stress testing might sound unfair, but it can happen that the user is flicking the view while the model gets updated resulting in an internally inconsistent state. We need to cover this and start working on a solution. The test is initially blacklisted as it's not expected to pass all the time. Task-number: QTBUG 48870 Change-Id: I7c1e4fd1876f52dd2dad5a28584b571753ebe69a Reviewed-by: Gabriel de Dietrich Reviewed-by: Shawn Rutledge --- src/quick/items/qquickitemview_p_p.h | 4 +- tests/auto/quick/qquicklistview/BLACKLIST | 2 + .../auto/quick/qquicklistview/data/qtbug48870.qml | 24 ++++ .../quick/qquicklistview/tst_qquicklistview.cpp | 132 +++++++++++++++++++++ 4 files changed, 160 insertions(+), 2 deletions(-) create mode 100644 tests/auto/quick/qquicklistview/data/qtbug48870.qml diff --git a/src/quick/items/qquickitemview_p_p.h b/src/quick/items/qquickitemview_p_p.h index 8af703eb03..ea1c7c494a 100644 --- a/src/quick/items/qquickitemview_p_p.h +++ b/src/quick/items/qquickitemview_p_p.h @@ -56,7 +56,7 @@ QT_BEGIN_NAMESPACE -class FxViewItem +class Q_AUTOTEST_EXPORT FxViewItem { public: FxViewItem(QQuickItem *, QQuickItemView *, bool own, QQuickItemViewAttached *attached); @@ -123,7 +123,7 @@ public: }; -class QQuickItemViewPrivate : public QQuickFlickablePrivate, public QQuickItemViewTransitionChangeListener, public QAnimationJobChangeListener +class Q_AUTOTEST_EXPORT QQuickItemViewPrivate : public QQuickFlickablePrivate, public QQuickItemViewTransitionChangeListener, public QAnimationJobChangeListener { Q_DECLARE_PUBLIC(QQuickItemView) public: diff --git a/tests/auto/quick/qquicklistview/BLACKLIST b/tests/auto/quick/qquicklistview/BLACKLIST index 269696ce8c..02adeacf05 100644 --- a/tests/auto/quick/qquicklistview/BLACKLIST +++ b/tests/auto/quick/qquicklistview/BLACKLIST @@ -2,3 +2,5 @@ * [enforceRange_withoutHighlight] osx +[QTBUG_48870_fastModelUpdates] +* diff --git a/tests/auto/quick/qquicklistview/data/qtbug48870.qml b/tests/auto/quick/qquicklistview/data/qtbug48870.qml new file mode 100644 index 0000000000..217f58af48 --- /dev/null +++ b/tests/auto/quick/qquicklistview/data/qtbug48870.qml @@ -0,0 +1,24 @@ +import QtQuick 2.6 + +Rectangle { + width: 500 + height: 500 + color: "blue" + + ListView { + objectName: "list" + anchors.fill: parent + model: testModel + + delegate: Rectangle { + height: 50 + width: ListView.view ? ListView.view.width : height + color: "green" + + Text { + anchors.centerIn: parent + text: "Item " + index + } + } + } +} diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp index a5de266636..ce2337ccb2 100644 --- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp +++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -249,6 +250,7 @@ private slots: void contentHeightWithDelayRemove_data(); void QTBUG_48044_currentItemNotVisibleAfterTransition(); + void QTBUG_48870_fastModelUpdates(); private: template void items(const QUrl &source); @@ -8295,6 +8297,136 @@ void tst_QQuickListView::QTBUG_48044_currentItemNotVisibleAfterTransition() QVERIFY(!currentPriv->culled); } +static bool 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 = Q_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; +} + +class QTBUG_48870_Model : public QAbstractListModel +{ + Q_OBJECT + +public: + + QTBUG_48870_Model() + : QAbstractListModel() + , m_rowCount(20) + { + QTimer *t = new QTimer(this); + t->setInterval(500); + t->start(); + + qsrand(qHash(QDateTime::currentDateTime())); + connect(t, &QTimer::timeout, this, &QTBUG_48870_Model::updateModel); + } + + int rowCount(const QModelIndex &) const + { + return m_rowCount; + } + + QVariant data(const QModelIndex &, int) const + { + return QVariant(); + } + +public Q_SLOTS: + void updateModel() + { + if (m_rowCount > 10) { + for (int i = 0; i < 10; ++i) { + int rnum = qrand() % m_rowCount; + beginRemoveRows(QModelIndex(), rnum, rnum); + m_rowCount--; + endRemoveRows(); + } + } + if (m_rowCount < 20) { + for (int i = 0; i < 10; ++i) { + int rnum = qrand() % m_rowCount; + beginInsertRows(QModelIndex(), rnum, rnum); + m_rowCount++; + endInsertRows(); + } + } + } + +private: + int m_rowCount; +}; + +void tst_QQuickListView::QTBUG_48870_fastModelUpdates() +{ + QTBUG_48870_Model model; + + QQuickView *window = createView(); + QVERIFY(window); + QQmlContext *ctxt = window->rootContext(); + QVERIFY(ctxt); + ctxt->setContextProperty("testModel", &model); + + window->setSource(testFileUrl("qtbug48870.qml")); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window)); + + QQuickListView *listview = findItem(window->rootObject(), "list"); + QTRY_VERIFY(listview != 0); + + QQuickItemViewPrivate *priv = QQuickItemViewPrivate::get(listview); + bool nonUnique; + FxViewItem *item = Q_NULLPTR; + int expectedIdx; + QVERIFY(testVisibleItems(priv, &nonUnique, &item, &expectedIdx)); + + for (int i = 0; i < 10; i++) { + QTest::qWait(100); + QVERIFY2(testVisibleItems(priv, &nonUnique, &item, &expectedIdx), + qPrintable(!item ? QString("Unexpected null item") + : nonUnique ? QString("Non-unique item at %1 and %2").arg(item->index).arg(expectedIdx) + : QString("Found index %1, expected index is %3").arg(item->index).arg(expectedIdx))); + if (i % 3 != 0) { + if (i & 1) + flick(window, QPoint(100, 200), QPoint(100, 0), 100); + else + flick(window, QPoint(100, 200), QPoint(100, 400), 100); + } + } + + delete window; +} + QTEST_MAIN(tst_QQuickListView) #include "tst_qquicklistview.moc" -- cgit v1.2.3