Ensure that header/footer resize fixes position.
[profile/ivi/qtdeclarative.git] / tests / auto / declarative / qsggridview / tst_qsggridview.cpp
index 76b3032..f29eb33 100644 (file)
@@ -77,6 +77,9 @@ private slots:
     void removed();
     void clear();
     void moved();
+    void moved_data();
+    void multipleChanges();
+    void multipleChanges_data();
     void swapWithFirstItem();
     void changeFlow();
     void currentIndex();
@@ -115,6 +118,29 @@ private:
     QList<T*> findItems(QSGItem *parent, const QString &objectName);
     void dumpTree(QSGItem *parent, int depth = 0);
 };
+
+template<typename T>
+void tst_qsggridview_move(int from, int to, int n, T *items)
+{
+    if (n == 1) {
+        items->move(from, to);
+    } else {
+        T replaced;
+        int i=0;
+        typename T::ConstIterator it=items->begin(); it += from+n;
+        for (; i<to-from; ++i,++it)
+            replaced.append(*it);
+        i=0;
+        it=items->begin(); it += from;
+        for (; i<n; ++i,++it)
+            replaced.append(*it);
+        typename T::ConstIterator f=replaced.begin();
+        typename T::Iterator t=items->begin(); t += from;
+        for (; f != replaced.end(); ++f, ++t)
+            *t = *f;
+    }
+}
+
 void tst_QSGGridView::initTestCase()
 {
     QSGView canvas;
@@ -126,6 +152,8 @@ void tst_QSGGridView::cleanupTestCase()
 {
 
 }
+
+
 class TestModel : public QAbstractListModel
 {
 public:
@@ -172,6 +200,13 @@ public:
         emit endInsertRows();
     }
 
+    void insertItems(int index, const QList<QPair<QString, QString> > &items) {
+        emit beginInsertRows(QModelIndex(), index, index + items.count() - 1);
+        for (int i=0; i<items.count(); i++)
+            list.insert(index + i, QPair<QString,QString>(items[i].first, items[i].second));
+        emit endInsertRows();
+    }
+
     void removeItem(int index) {
         emit beginRemoveRows(QModelIndex(), index, index);
         list.removeAt(index);
@@ -191,6 +226,12 @@ public:
         emit endMoveRows();
     }
 
+    void moveItems(int from, int to, int count) {
+        emit beginMoveRows(QModelIndex(), from, from+count-1, QModelIndex(), to > from ? to+count : to);
+        tst_qsggridview_move(from, to, count, &list);
+        emit endMoveRows();
+    }
+
     void modifyItem(int idx, const QString &name, const QString &number) {
         list[idx] = QPair<QString,QString>(name, number);
         emit dataChanged(index(idx,0), index(idx,0));
@@ -325,8 +366,8 @@ void tst_QSGGridView::inserted()
     QTRY_VERIFY(contentItem != 0);
 
     model.insertItem(1, "Will", "9876");
-    QCOMPARE(canvas->rootObject()->property("count").toInt(), model.count());
 
+    QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
     QTRY_COMPARE(contentItem->childItems().count(), model.count()+1); // assumes all are visible, +1 for the (default) highlight item
 
     QSGText *name = findItem<QSGText>(contentItem, "textName", 1);
@@ -404,7 +445,7 @@ void tst_QSGGridView::removed()
     QTRY_VERIFY(contentItem != 0);
 
     model.removeItem(1);
-    QCOMPARE(canvas->rootObject()->property("count").toInt(), model.count());
+    QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
 
     QSGText *name = findItem<QSGText>(contentItem, "textName", 1);
     QTRY_VERIFY(name != 0);
@@ -413,6 +454,7 @@ void tst_QSGGridView::removed()
     QTRY_VERIFY(number != 0);
     QTRY_COMPARE(number->text(), model.number(1));
 
+
     // Checks that onRemove is called
     QString removed = canvas->rootObject()->property("removed").toString();
     QTRY_COMPARE(removed, QString("Item1"));
@@ -422,13 +464,13 @@ void tst_QSGGridView::removed()
     for (int i = 0; i < model.count() && i < itemCount; ++i) {
         QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
         if (!item) qWarning() << "Item" << i << "not found";
-        QTRY_VERIFY(item);
         QTRY_VERIFY(item->x() == (i%3)*80);
         QTRY_VERIFY(item->y() == (i/3)*60);
     }
 
     // Remove first item (which is the current item);
     model.removeItem(0);
+    QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
 
     name = findItem<QSGText>(contentItem, "textName", 0);
     QTRY_VERIFY(name != 0);
@@ -437,25 +479,25 @@ void tst_QSGGridView::removed()
     QTRY_VERIFY(number != 0);
     QTRY_COMPARE(number->text(), model.number(0));
 
+
     // Confirm items positioned correctly
     itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
     for (int i = 0; i < model.count() && i < itemCount; ++i) {
         QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
         if (!item) qWarning() << "Item" << i << "not found";
-        QTRY_VERIFY(item);
         QTRY_VERIFY(item->x() == (i%3)*80);
         QTRY_VERIFY(item->y() == (i/3)*60);
     }
 
     // Remove items not visible
     model.removeItem(25);
+    QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
 
     // Confirm items positioned correctly
     itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
     for (int i = 0; i < model.count() && i < itemCount; ++i) {
         QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
         if (!item) qWarning() << "Item" << i << "not found";
-        QTRY_VERIFY(item);
         QTRY_VERIFY(item->x() == (i%3)*80);
         QTRY_VERIFY(item->y() == (i/3)*60);
     }
@@ -468,12 +510,12 @@ void tst_QSGGridView::removed()
     QTRY_COMPARE(gridview->contentY(), 120.0);
 
     model.removeItem(1);
+    QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
 
     // Confirm items positioned correctly
     for (int i = 6; i < 18; ++i) {
         QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
         if (!item) qWarning() << "Item" << i << "not found";
-        QTRY_VERIFY(item);
         QTRY_VERIFY(item->x() == (i%3)*80);
         QTRY_VERIFY(item->y() == (i/3)*60);
     }
@@ -481,20 +523,19 @@ void tst_QSGGridView::removed()
     // Remove currentIndex
     QSGItem *oldCurrent = gridview->currentItem();
     model.removeItem(9);
+    QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
 
     QTRY_COMPARE(gridview->currentIndex(), 9);
     QTRY_VERIFY(gridview->currentItem() != oldCurrent);
 
     gridview->setContentY(0);
     // let transitions settle.
-    QTest::qWait(100);
+    QTest::qWait(300);
 
     // Confirm items positioned correctly
     itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
     for (int i = 0; i < model.count() && i < itemCount; ++i) {
         QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
-        if (!item) qWarning() << "Item" << i << "not found";
-        QTRY_VERIFY(item);
         QTRY_VERIFY(item->x() == (i%3)*80);
         QTRY_VERIFY(item->y() == (i/3)*60);
     }
@@ -557,7 +598,7 @@ void tst_QSGGridView::clear()
 
     // confirm sanity when adding an item to cleared list
     model.addItem("New", "1");
-    QVERIFY(gridview->count() == 1);
+    QTRY_COMPARE(gridview->count(), 1);
     QVERIFY(gridview->currentItem() != 0);
     QVERIFY(gridview->currentIndex() == 0);
 
@@ -566,7 +607,16 @@ void tst_QSGGridView::clear()
 
 void tst_QSGGridView::moved()
 {
+    QFETCH(qreal, contentY);
+    QFETCH(int, from);
+    QFETCH(int, to);
+    QFETCH(int, count);
+    QFETCH(qreal, itemsOffsetAfterMove);
+
+    QSGText *name;
+    QSGText *number;
     QSGView *canvas = createView();
+    canvas->show();
 
     TestModel model;
     for (int i = 0; i < 30; i++)
@@ -586,74 +636,396 @@ void tst_QSGGridView::moved()
     QSGItem *contentItem = gridview->contentItem();
     QTRY_VERIFY(contentItem != 0);
 
-    model.moveItem(1, 8);
+    QSGItem *currentItem = gridview->currentItem();
+    QTRY_VERIFY(currentItem != 0);
 
-    QSGText *name = findItem<QSGText>(contentItem, "textName", 1);
-    QTRY_VERIFY(name != 0);
-    QTRY_COMPARE(name->text(), model.name(1));
-    QSGText *number = findItem<QSGText>(contentItem, "textNumber", 1);
-    QTRY_VERIFY(number != 0);
-    QTRY_COMPARE(number->text(), model.number(1));
+    gridview->setContentY(contentY);
+    model.moveItems(from, to, count);
 
-    name = findItem<QSGText>(contentItem, "textName", 8);
-    QTRY_VERIFY(name != 0);
-    QTRY_COMPARE(name->text(), model.name(8));
-    number = findItem<QSGText>(contentItem, "textNumber", 8);
-    QTRY_VERIFY(number != 0);
-    QTRY_COMPARE(number->text(), model.number(8));
+    // wait for items to move
+    QTest::qWait(300);
 
-    // Confirm items positioned correctly
+    // Confirm items positioned correctly and indexes correct
+    int firstVisibleIndex = qCeil(contentY / 60.0) * 3;
     int itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
-    for (int i = 0; i < model.count() && i < itemCount; ++i) {
+    for (int i = firstVisibleIndex; i < model.count() && i < itemCount; ++i) {
+        if (i >= firstVisibleIndex + 18)    // index has moved out of view
+            continue;
         QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
-        if (!item) qWarning() << "Item" << i << "not found";
-        QTRY_VERIFY(item);
-        QTRY_VERIFY(item->x() == (i%3)*80);
-        QTRY_VERIFY(item->y() == (i/3)*60);
-    }
+        QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
 
-    gridview->setContentY(120);
-
-    // move outside visible area
-    model.moveItem(1, 25);
+        QTRY_COMPARE(item->x(), (i%3)*80.0);
+        QTRY_COMPARE(item->y(), (i/3)*60.0 + itemsOffsetAfterMove);
 
-    // Confirm items positioned correctly and indexes correct
-    itemCount = findItems<QSGItem>(contentItem, "wrapper").count()-1;
-    for (int i = 6; i < model.count()-6 && i < itemCount+6; ++i) {
-        QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
-        if (!item) qWarning() << "Item" << i << "not found";
-        QTRY_VERIFY(item);
-        QTRY_COMPARE(item->x(), qreal((i%3)*80));
-        QTRY_COMPARE(item->y(), qreal((i/3)*60));
         name = findItem<QSGText>(contentItem, "textName", i);
-        QTRY_VERIFY(name != 0);
+        QVERIFY(name != 0);
         QTRY_COMPARE(name->text(), model.name(i));
         number = findItem<QSGText>(contentItem, "textNumber", i);
-        QTRY_VERIFY(number != 0);
+        QVERIFY(number != 0);
         QTRY_COMPARE(number->text(), model.number(i));
+
+        // current index should have been updated
+        if (item == currentItem)
+            QTRY_COMPARE(gridview->currentIndex(), i);
     }
 
-    // move from outside visible into visible
-    model.moveItem(28, 8);
+    delete canvas;
+}
 
-    // Confirm items positioned correctly and indexes correct
-    for (int i = 6; i < model.count()-6 && i < itemCount+6; ++i) {
+void tst_QSGGridView::moved_data()
+{
+    QTest::addColumn<qreal>("contentY");
+    QTest::addColumn<int>("from");
+    QTest::addColumn<int>("to");
+    QTest::addColumn<int>("count");
+    QTest::addColumn<qreal>("itemsOffsetAfterMove");
+
+    // model starts with 30 items, each 80x60, in area 240x320
+    // 18 items should be visible at a time
+
+    QTest::newRow("move 1 forwards, within visible items")
+            << 0.0
+            << 1 << 8 << 1
+            << 0.0;
+
+    QTest::newRow("move 1 forwards, from non-visible -> visible")
+            << 120.0     // show 6-23
+            << 1 << 23 << 1
+            << 0.0;     // only 1 item was removed from the 1st row, so it doesn't move down
+
+    QTest::newRow("move 1 forwards, from non-visible -> visible (move first item)")
+            << 120.0     // // show 6-23
+            << 0 << 6 << 1
+            << 0.0;     // only 1 item was removed from the 1st row, so it doesn't move down
+
+    QTest::newRow("move 1 forwards, from visible -> non-visible")
+            << 0.0
+            << 1 << 20 << 1
+            << 0.0;
+
+    QTest::newRow("move 1 forwards, from visible -> non-visible (move first item)")
+            << 0.0
+            << 0 << 20 << 1
+            << 0.0;
+
+
+    QTest::newRow("move 1 backwards, within visible items")
+            << 0.0
+            << 10 << 5 << 1
+            << 0.0;
+
+    QTest::newRow("move 1 backwards, within visible items (to first index)")
+            << 0.0
+            << 10 << 0 << 1
+            << 0.0;
+
+    QTest::newRow("move 1 backwards, from non-visible -> visible")
+            << 0.0
+            << 28 << 8 << 1
+            << 0.0;
+
+    QTest::newRow("move 1 backwards, from non-visible -> visible (move last item)")
+            << 0.0
+            << 29 << 14 << 1
+            << 0.0;
+
+    QTest::newRow("move 1 backwards, from visible -> non-visible")
+            << 120.0     // show 6-23
+            << 7 << 1 << 1
+            << 0.0;     // only 1 item moved back, so items shift accordingly and first row doesn't move
+
+    QTest::newRow("move 1 backwards, from visible -> non-visible (move first item)")
+            << 120.0     // show 6-23
+            << 7 << 0 << 1
+            << 0.0;     // only 1 item moved back, so items shift accordingly and first row doesn't move
+
+
+    QTest::newRow("move multiple forwards, within visible items")
+            << 0.0
+            << 0 << 5 << 3
+            << 60.0;    // moved 3 items (i.e. 1 row) down
+
+    QTest::newRow("move multiple forwards, from non-visible -> visible")
+            << 120.0     // show 6-23
+            << 1 << 6 << 3
+            << 60.0; // 1st row (it's above visible area) disappears, 0 drops down 1 row, first visible item (6) stays where it is
+
+    QTest::newRow("move multiple forwards, from non-visible -> visible (move first item)")
+            << 120.0     // show 6-23
+            << 0 << 6 << 3
+            << 60.0;    // top row moved and shifted to below 3rd row, all items should shift down by 1 row
+
+    QTest::newRow("move multiple forwards, from visible -> non-visible")
+            << 0.0
+            << 1 << 16 << 3
+            << 0.0;
+
+    QTest::newRow("move multiple forwards, from visible -> non-visible (move first item)")
+            << 0.0
+            << 0 << 16 << 3
+            << 60.0;
+
+
+    QTest::newRow("move multiple backwards, within visible items")
+            << 0.0
+            << 4 << 1 << 3
+            << 0.0;
+
+    QTest::newRow("move multiple backwards, from non-visible -> visible")
+            << 0.0
+            << 20 << 4 << 3
+            << 0.0;
+
+    QTest::newRow("move multiple backwards, from non-visible -> visible (move last item)")
+            << 0.0
+            << 27 << 10 << 3
+            << 0.0;
+
+    QTest::newRow("move multiple backwards, from visible -> non-visible")
+            << 120.0     // show 6-23
+            << 16 << 1 << 3
+            << -60.0;   // to minimize movement, items are added above visible area, all items move up by 1 row
+
+    QTest::newRow("move multiple backwards, from visible -> non-visible (move first item)")
+            << 120.0     // show 6-23
+            << 16 << 0 << 3
+            << -60.0;   // 16,17,18 move to above item 0, all items move up by 1 row
+}
+
+struct ListChange {
+    enum { Inserted, Removed, Moved, SetCurrent } type;
+    int index;
+    int count;
+    int to;     // Move
+
+    static ListChange insert(int index, int count = 1) { ListChange c = { Inserted, index, count, -1 }; return c; }
+    static ListChange remove(int index, int count = 1) { ListChange c = { Removed, index, count, -1 }; return c; }
+    static ListChange move(int index, int to, int count) { ListChange c = { Moved, index, count, to }; return c; }
+    static ListChange setCurrent(int index) { ListChange c = { SetCurrent, index, -1, -1 }; return c; }
+};
+Q_DECLARE_METATYPE(QList<ListChange>)
+
+void tst_QSGGridView::multipleChanges()
+{
+    QFETCH(int, startCount);
+    QFETCH(QList<ListChange>, changes);
+    QFETCH(int, newCount);
+    QFETCH(int, newCurrentIndex);
+
+    QSGView *canvas = createView();
+    canvas->show();
+
+    TestModel model;
+    for (int i = 0; i < startCount; i++)
+        model.addItem("Item" + QString::number(i), "");
+
+    QDeclarativeContext *ctxt = canvas->rootContext();
+    ctxt->setContextProperty("testModel", &model);
+    ctxt->setContextProperty("testRightToLeft", QVariant(false));
+    ctxt->setContextProperty("testTopToBottom", QVariant(false));
+
+    canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/gridview1.qml"));
+    qApp->processEvents();
+
+    QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
+    QTRY_VERIFY(gridview != 0);
+
+    for (int i=0; i<changes.count(); i++) {
+        switch (changes[i].type) {
+            case ListChange::Inserted:
+            {
+                QList<QPair<QString, QString> > items;
+                for (int j=changes[i].index; j<changes[i].index + changes[i].count; ++j)
+                    items << qMakePair(QString("new item " + j), QString::number(j));
+                model.insertItems(changes[i].index, items);
+                break;
+            }
+            case ListChange::Removed:
+                model.removeItems(changes[i].index, changes[i].count);
+                break;
+            case ListChange::Moved:
+                model.moveItems(changes[i].index, changes[i].to, changes[i].count);
+                break;
+            case ListChange::SetCurrent:
+                gridview->setCurrentIndex(changes[i].index);
+                break;
+        }
+    }
+
+    QTRY_COMPARE(gridview->count(), newCount);
+    QCOMPARE(gridview->count(), model.count());
+    QTRY_COMPARE(gridview->currentIndex(), newCurrentIndex);
+
+    QSGText *name;
+    QSGText *number;
+    QSGItem *contentItem = gridview->contentItem();
+    QTRY_VERIFY(contentItem != 0);
+    int itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
+    for (int i=0; i < model.count() && i < itemCount; ++i) {
         QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
-        if (!item) qWarning() << "Item" << i << "not found";
-        QTRY_VERIFY(item);
-        QTRY_VERIFY(item->x() == (i%3)*80);
-        QTRY_VERIFY(item->y() == (i/3)*60);
+        QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
         name = findItem<QSGText>(contentItem, "textName", i);
-        QTRY_VERIFY(name != 0);
+        QVERIFY(name != 0);
         QTRY_COMPARE(name->text(), model.name(i));
         number = findItem<QSGText>(contentItem, "textNumber", i);
-        QTRY_VERIFY(number != 0);
+        QVERIFY(number != 0);
         QTRY_COMPARE(number->text(), model.number(i));
     }
 
     delete canvas;
 }
 
+void tst_QSGGridView::multipleChanges_data()
+{
+    QTest::addColumn<int>("startCount");
+    QTest::addColumn<QList<ListChange> >("changes");
+    QTest::addColumn<int>("newCount");
+    QTest::addColumn<int>("newCurrentIndex");
+
+    QList<ListChange> changes;
+
+    for (int i=1; i<30; i++)
+        changes << ListChange::remove(0);
+    QTest::newRow("remove all but 1, first->last") << 30 << changes << 1 << 0;
+
+    changes << ListChange::remove(0);
+    QTest::newRow("remove all") << 30 << changes << 0 << -1;
+
+    changes.clear();
+    changes << ListChange::setCurrent(29);
+    for (int i=29; i>0; i--)
+        changes << ListChange::remove(i);
+    QTest::newRow("remove last (current) -> first") << 30 << changes << 1 << 0;
+
+    QTest::newRow("remove then insert at 0") << 10 << (QList<ListChange>()
+            << ListChange::remove(0, 1)
+            << ListChange::insert(0, 1)
+            ) << 10 << 1;
+
+    QTest::newRow("remove then insert at non-zero index") << 10 << (QList<ListChange>()
+            << ListChange::setCurrent(2)
+            << ListChange::remove(2, 1)
+            << ListChange::insert(2, 1)
+            ) << 10 << 3;
+
+    QTest::newRow("remove current then insert below it") << 10 << (QList<ListChange>()
+            << ListChange::setCurrent(1)
+            << ListChange::remove(1, 3)
+            << ListChange::insert(2, 2)
+            ) << 9 << 1;
+
+    QTest::newRow("remove current index then move it down") << 10 << (QList<ListChange>()
+            << ListChange::setCurrent(2)
+            << ListChange::remove(1, 3)
+            << ListChange::move(1, 5, 1)
+            ) << 7 << 5;
+
+    QTest::newRow("remove current index then move it up") << 10 << (QList<ListChange>()
+            << ListChange::setCurrent(5)
+            << ListChange::remove(4, 3)
+            << ListChange::move(4, 1, 1)
+            ) << 7 << 1;
+
+
+    QTest::newRow("insert multiple times") << 0 << (QList<ListChange>()
+            << ListChange::insert(0, 2)
+            << ListChange::insert(0, 4)
+            << ListChange::insert(0, 6)
+            ) << 12 << 10;
+
+    QTest::newRow("insert multiple times with current index changes") << 0 << (QList<ListChange>()
+            << ListChange::insert(0, 2)
+            << ListChange::insert(0, 4)
+            << ListChange::insert(0, 6)
+            << ListChange::setCurrent(3)
+            << ListChange::insert(3, 2)
+            ) << 14 << 5;
+
+    QTest::newRow("insert and remove all") << 0 << (QList<ListChange>()
+            << ListChange::insert(0, 30)
+            << ListChange::remove(0, 30)
+            ) << 0 << -1;
+
+    QTest::newRow("insert and remove current") << 30 << (QList<ListChange>()
+            << ListChange::insert(1)
+            << ListChange::setCurrent(1)
+            << ListChange::remove(1)
+            ) << 30 << 1;
+
+    QTest::newRow("insert before 0, then remove cross section of new and old items") << 10 << (QList<ListChange>()
+            << ListChange::insert(0, 10)
+            << ListChange::remove(5, 10)
+            ) << 10 << 5;
+
+    QTest::newRow("insert multiple, then move new items to end") << 10 << (QList<ListChange>()
+            << ListChange::insert(0, 3)
+            << ListChange::move(0, 10, 3)
+            ) << 13 << 0;
+
+    QTest::newRow("insert multiple, then move new and some old items to end") << 10 << (QList<ListChange>()
+            << ListChange::insert(0, 3)
+            << ListChange::move(0, 8, 5)
+            ) << 13 << 11;
+
+    QTest::newRow("insert multiple at end, then move new and some old items to start") << 10 << (QList<ListChange>()
+            << ListChange::setCurrent(9)
+            << ListChange::insert(10, 3)
+            << ListChange::move(8, 0, 5)
+            ) << 13 << 1;
+
+
+    QTest::newRow("move back and forth to same index") << 10 << (QList<ListChange>()
+            << ListChange::setCurrent(1)
+            << ListChange::move(1, 2, 2)
+            << ListChange::move(2, 1, 2)
+            ) << 10 << 1;
+
+    QTest::newRow("move forwards then back") << 10 << (QList<ListChange>()
+            << ListChange::setCurrent(2)
+            << ListChange::move(1, 2, 3)
+            << ListChange::move(3, 0, 5)
+            ) << 10 << 0;
+
+    QTest::newRow("move current, then remove it") << 10 << (QList<ListChange>()
+            << ListChange::setCurrent(5)
+            << ListChange::move(5, 0, 1)
+            << ListChange::remove(0)
+            ) << 9 << 0;
+
+    QTest::newRow("move current, then insert before it") << 10 << (QList<ListChange>()
+            << ListChange::setCurrent(5)
+            << ListChange::move(5, 0, 1)
+            << ListChange::insert(0)
+            ) << 11 << 1;
+
+    QTest::newRow("move multiple, then remove them") << 10 << (QList<ListChange>()
+            << ListChange::setCurrent(1)
+            << ListChange::move(5, 1, 3)
+            << ListChange::remove(1, 3)
+            ) << 7 << 1;
+
+    QTest::newRow("move multiple, then insert before them") << 10 << (QList<ListChange>()
+            << ListChange::setCurrent(5)
+            << ListChange::move(5, 1, 3)
+            << ListChange::insert(1, 5)
+            ) << 15 << 6;
+
+    QTest::newRow("move multiple, then insert after them") << 10 << (QList<ListChange>()
+            << ListChange::setCurrent(3)
+            << ListChange::move(0, 1, 2)
+            << ListChange::insert(3, 5)
+            ) << 15 << 8;
+
+
+    QTest::newRow("clear current") << 0 << (QList<ListChange>()
+            << ListChange::insert(0, 5)
+            << ListChange::setCurrent(-1)
+            << ListChange::remove(0, 5)
+            << ListChange::insert(0, 5)
+            ) << 5 << -1;
+}
+
+
 void tst_QSGGridView::swapWithFirstItem()
 {
     // QTBUG_9697
@@ -760,6 +1132,26 @@ void tst_QSGGridView::currentIndex()
 
     QTRY_COMPARE(gridview->contentY(), 0.0);
 
+
+    // footer should become visible if it is out of view, and then current index moves to the first row
+    canvas->rootObject()->setProperty("showFooter", true);
+    QTRY_VERIFY(gridview->footerItem());
+    gridview->setCurrentIndex(model.count()-3);
+    QTRY_VERIFY(gridview->footerItem()->y() > gridview->contentY() + gridview->height());
+    gridview->setCurrentIndex(model.count()-2);
+    QTRY_COMPARE(gridview->contentY() + gridview->height(), (60.0 * model.count()/3) + gridview->footerItem()->height());
+    canvas->rootObject()->setProperty("showFooter", false);
+
+    // header should become visible if it is out of view, and then current index moves to the last row
+    canvas->rootObject()->setProperty("showHeader", true);
+    QTRY_VERIFY(gridview->headerItem());
+    gridview->setCurrentIndex(3);
+    QTRY_VERIFY(gridview->headerItem()->y() + gridview->headerItem()->height() < gridview->contentY());
+    gridview->setCurrentIndex(1);
+    QTRY_COMPARE(gridview->contentY(), -gridview->headerItem()->height());
+    canvas->rootObject()->setProperty("showHeader", false);
+
+
     // Test keys
     qApp->setActiveWindow(canvas);
 #ifdef Q_WS_X11
@@ -769,12 +1161,33 @@ void tst_QSGGridView::currentIndex()
     QTRY_VERIFY(canvas->hasFocus());
     qApp->processEvents();
 
+    gridview->setCurrentIndex(0);
+
     QTest::keyClick(canvas, Qt::Key_Down);
     QCOMPARE(gridview->currentIndex(), 3);
 
     QTest::keyClick(canvas, Qt::Key_Up);
     QCOMPARE(gridview->currentIndex(), 0);
 
+    // hold down Key_Down
+    for (int i=0; i<(model.count() / 3) - 1; i++) {
+        QTest::simulateEvent(canvas, true, Qt::Key_Down, Qt::NoModifier, "", true);
+        QTRY_COMPARE(gridview->currentIndex(), i*3 + 3);
+    }
+    QTest::keyRelease(canvas, Qt::Key_Down);
+    QTRY_COMPARE(gridview->currentIndex(), 57);
+    QTRY_COMPARE(gridview->contentY(), 880.0);
+
+    // hold down Key_Up
+    for (int i=(model.count() / 3) - 1; i > 0; i--) {
+        QTest::simulateEvent(canvas, true, Qt::Key_Up, Qt::NoModifier, "", true);
+        QTRY_COMPARE(gridview->currentIndex(), i*3 - 3);
+    }
+    QTest::keyRelease(canvas, Qt::Key_Up);
+    QTRY_COMPARE(gridview->currentIndex(), 0);
+    QTRY_COMPARE(gridview->contentY(), 0.0);
+
+
     gridview->setFlow(QSGGridView::TopToBottom);
 
     qApp->setActiveWindow(canvas);
@@ -797,6 +1210,24 @@ void tst_QSGGridView::currentIndex()
     QTest::keyClick(canvas, Qt::Key_Up);
     QCOMPARE(gridview->currentIndex(), 0);
 
+    // hold down Key_Right
+    for (int i=0; i<(model.count() / 5) - 1; i++) {
+        QTest::simulateEvent(canvas, true, Qt::Key_Right, Qt::NoModifier, "", true);
+        QTRY_COMPARE(gridview->currentIndex(), i*5 + 5);
+    }
+    QTest::keyRelease(canvas, Qt::Key_Right);
+    QTRY_COMPARE(gridview->currentIndex(), 55);
+    QTRY_COMPARE(gridview->contentX(), 720.0);
+
+    // hold down Key_Left
+    for (int i=(model.count() / 5) - 1; i > 0; i--) {
+        QTest::simulateEvent(canvas, true, Qt::Key_Left, Qt::NoModifier, "", true);
+        QTRY_COMPARE(gridview->currentIndex(), i*5 - 5);
+    }
+    QTest::keyRelease(canvas, Qt::Key_Left);
+    QTRY_COMPARE(gridview->currentIndex(), 0);
+    QTRY_COMPARE(gridview->contentX(), 0.0);
+
 
     // turn off auto highlight
     gridview->setHighlightFollowsCurrentItem(false);
@@ -1187,7 +1618,7 @@ void tst_QSGGridView::modelChanges()
 
     QDeclarativeListModel *alternateModel = canvas->rootObject()->findChild<QDeclarativeListModel*>("alternateModel");
     QTRY_VERIFY(alternateModel);
-    QVariant modelVariant = QVariant::fromValue(alternateModel);
+    QVariant modelVariant = QVariant::fromValue<QObject *>(alternateModel);
     QSignalSpy modelSpy(gridView, SIGNAL(modelChanged()));
 
     gridView->setModel(modelVariant);
@@ -1865,6 +2296,7 @@ void tst_QSGGridView::footer()
     QFETCH(QPointF, initialContentPos);
     QFETCH(QPointF, changedContentPos);
     QFETCH(QPointF, firstDelegatePos);
+    QFETCH(QPointF, resizeContentPos);
 
     QSGView *canvas = createView();
     canvas->show();
@@ -1954,6 +2386,11 @@ void tst_QSGGridView::footer()
     QVERIFY(item);
     QCOMPARE(item->pos(), firstDelegatePos);
 
+    gridview->positionViewAtEnd();
+    footer->setHeight(10);
+    footer->setWidth(40);
+    QTRY_COMPARE(QPointF(gridview->contentX(), gridview->contentY()), resizeContentPos);
+
     delete canvas;
 }
 
@@ -1966,11 +2403,13 @@ void tst_QSGGridView::footer_data()
     QTest::addColumn<QPointF>("initialContentPos");
     QTest::addColumn<QPointF>("changedContentPos");
     QTest::addColumn<QPointF>("firstDelegatePos");
+    QTest::addColumn<QPointF>("resizeContentPos");
 
     // footer1 = 100 x 30
-    // footer2 = 100 x 20
+    // footer2 = 50 x 20
     // cells = 80 * 60
     // view width = 240
+    // view height = 320
 
     // footer below items, bottom left
     QTest::newRow("flow left to right") << QSGGridView::LeftToRight << Qt::LeftToRight
@@ -1978,7 +2417,8 @@ void tst_QSGGridView::footer_data()
         << QPointF(0, 10 * 60)  // 30 items = 10 rows
         << QPointF(0, 0)
         << QPointF(0, 0)
-        << QPointF(0, 0);
+        << QPointF(0, 0)
+        << QPointF(0, 10 * 60 - 320 + 10);
 
     // footer below items, bottom right
     QTest::newRow("flow left to right, layout right to left") << QSGGridView::LeftToRight << Qt::RightToLeft
@@ -1986,7 +2426,8 @@ void tst_QSGGridView::footer_data()
         << QPointF((240 - 100) + 50, 10 * 60)     // 50 = width diff between old and new footers
         << QPointF(0, 0)
         << QPointF(0, 0)
-        << QPointF(240 - 80, 0);
+        << QPointF(240 - 80, 0)
+        << QPointF(0, 10 * 60 - 320 + 10);
 
     // footer to right of items
     QTest::newRow("flow top to bottom, layout left to right") << QSGGridView::TopToBottom << Qt::LeftToRight
@@ -1994,7 +2435,8 @@ void tst_QSGGridView::footer_data()
         << QPointF(6 * 80, 0)      // 30 items = 6 columns
         << QPointF(0, 0)
         << QPointF(0, 0)
-        << QPointF(0, 0);
+        << QPointF(0, 0)
+        << QPointF(6 * 80 - 240 + 40, 0);
 
     // footer to left of items
     QTest::newRow("flow top to bottom, layout right to left") << QSGGridView::TopToBottom << Qt::RightToLeft
@@ -2002,7 +2444,8 @@ void tst_QSGGridView::footer_data()
         << QPointF(-(6 * 80) - 50, 0)     // 50 = new footer width
         << QPointF(-240, 0)
         << QPointF(-240, 0)    // unchanged, footer change doesn't change content pos
-        << QPointF(-80, 0);
+        << QPointF(-80, 0)
+        << QPointF(-(6 * 80) - 40, 0);
 }
 
 void tst_QSGGridView::header()
@@ -2014,6 +2457,7 @@ void tst_QSGGridView::header()
     QFETCH(QPointF, initialContentPos);
     QFETCH(QPointF, changedContentPos);
     QFETCH(QPointF, firstDelegatePos);
+    QFETCH(QPointF, resizeContentPos);
 
     QSGView *canvas = createView();
 
@@ -2076,6 +2520,10 @@ void tst_QSGGridView::header()
     QVERIFY(item);
     QCOMPARE(item->pos(), firstDelegatePos);
 
+    header->setHeight(10);
+    header->setWidth(40);
+    QTRY_COMPARE(QPointF(gridview->contentX(), gridview->contentY()), resizeContentPos);
+
     delete canvas;
 }
 
@@ -2088,9 +2536,10 @@ void tst_QSGGridView::header_data()
     QTest::addColumn<QPointF>("initialContentPos");
     QTest::addColumn<QPointF>("changedContentPos");
     QTest::addColumn<QPointF>("firstDelegatePos");
+    QTest::addColumn<QPointF>("resizeContentPos");
 
     // header1 = 100 x 30
-    // header2 = 100 x 20
+    // header2 = 50 x 20
     // cells = 80 x 60
     // view width = 240
 
@@ -2100,7 +2549,8 @@ void tst_QSGGridView::header_data()
         << QPointF(0, -20)
         << QPointF(0, -30)
         << QPointF(0, -20)
-        << QPointF(0, 0);
+        << QPointF(0, 0)
+        << QPointF(0, -10);
 
     // header above items, top right
     QTest::newRow("flow left to right, layout right to left") << QSGGridView::LeftToRight << Qt::RightToLeft
@@ -2108,7 +2558,8 @@ void tst_QSGGridView::header_data()
         << QPointF((240 - 100) + 50, -20)     // 50 = width diff between old and new headers
         << QPointF(0, -30)
         << QPointF(0, -20)
-        << QPointF(160, 0);
+        << QPointF(160, 0)
+        << QPointF(0, -10);
 
     // header to left of items
     QTest::newRow("flow top to bottom, layout left to right") << QSGGridView::TopToBottom << Qt::LeftToRight
@@ -2116,7 +2567,8 @@ void tst_QSGGridView::header_data()
         << QPointF(-50, 0)
         << QPointF(-100, 0)
         << QPointF(-50, 0)
-        << QPointF(0, 0);
+        << QPointF(0, 0)
+        << QPointF(-40, 0);
 
     // header to right of items
     QTest::newRow("flow top to bottom, layout right to left") << QSGGridView::TopToBottom << Qt::RightToLeft
@@ -2124,7 +2576,8 @@ void tst_QSGGridView::header_data()
         << QPointF(0, 0)
         << QPointF(-(240 - 100), 0)
         << QPointF(-(240 - 50), 0)
-        << QPointF(-80, 0);
+        << QPointF(-80, 0)
+        << QPointF(-(240 - 40), 0);
 }
 
 void tst_QSGGridView::indexAt()
@@ -2196,10 +2649,11 @@ void tst_QSGGridView::onAdd()
         items << qMakePair(QString("value %1").arg(i), QString::number(i));
     model.addItems(items);
 
+    QTRY_COMPARE(model.count(), qobject_cast<QSGGridView*>(canvas->rootObject())->count());
     qApp->processEvents();
 
     QVariantList result = object->property("addedDelegates").toList();
-    QCOMPARE(result.count(), items.count());
+    QTRY_COMPARE(result.count(), items.count());
     for (int i=0; i<items.count(); i++)
         QCOMPARE(result[i].toString(), items[i].first);
 
@@ -2244,10 +2698,8 @@ void tst_QSGGridView::onRemove()
     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/attachedSignals.qml"));
     QObject *object = canvas->rootObject();
 
-    qApp->processEvents();
-
     model.removeItems(indexToRemove, removeCount);
-    qApp->processEvents();
+    QTRY_COMPARE(model.count(), qobject_cast<QSGGridView*>(canvas->rootObject())->count());
     QCOMPARE(object->property("removedDelegateCount"), QVariant(removeCount));
 
     delete canvas;
@@ -2395,3 +2847,4 @@ void tst_QSGGridView::dumpTree(QSGItem *parent, int depth)
 QTEST_MAIN(tst_QSGGridView)
 
 #include "tst_qsggridview.moc"
+