diff options
Diffstat (limited to 'tests/auto/qquicktreemodeladaptor/tst_qquicktreemodeladaptor.cpp')
-rw-r--r-- | tests/auto/qquicktreemodeladaptor/tst_qquicktreemodeladaptor.cpp | 1135 |
1 files changed, 1135 insertions, 0 deletions
diff --git a/tests/auto/qquicktreemodeladaptor/tst_qquicktreemodeladaptor.cpp b/tests/auto/qquicktreemodeladaptor/tst_qquicktreemodeladaptor.cpp new file mode 100644 index 00000000..ea082bc0 --- /dev/null +++ b/tests/auto/qquicktreemodeladaptor/tst_qquicktreemodeladaptor.cpp @@ -0,0 +1,1135 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> +#include <QtCore> +#include <qquicktreemodeladaptor_p.h> +#include "../shared/testmodel.h" + +class tst_QQuickTreeModelAdaptor : public QObject +{ + Q_OBJECT + +public: + void compareData(int row, QQuickTreeModelAdaptor &tma, const QModelIndex &idx, TestModel &model, bool expanded = false); + void compareModels(QQuickTreeModelAdaptor &tma, TestModel &model); + void expandAndTest(const QModelIndex &idx, QQuickTreeModelAdaptor &tma, bool expandable, int expectedRowCountDifference); + void collapseAndTest(const QModelIndex &idx, QQuickTreeModelAdaptor &tma, bool expandable, int expectedRowCountDifference); + +private slots: + void initTestCase(); + void cleanup(); + + void setModel(); + void modelReset(); + + void dataAccess(); + void dataChange(); + void groupedDataChange(); + + void expandAndCollapse_data(); + void expandAndCollapse(); + void expandAndCollapse2ndLevel(); + + void layoutChange(); + + void removeRows_data(); + void removeRows(); + + void insertRows_data(); + void insertRows(); + + void moveRows_data(); + void moveRows(); + + void selectionForRowRange(); +}; + +void tst_QQuickTreeModelAdaptor::initTestCase() +{ +} + +void tst_QQuickTreeModelAdaptor::cleanup() +{ +} + +void tst_QQuickTreeModelAdaptor::compareData(int row, QQuickTreeModelAdaptor &tma, const QModelIndex &modelIdx, TestModel &model, bool expanded) +{ + const QModelIndex &tmaIdx = tma.index(row); + QCOMPARE(tma.mapToModel(tmaIdx), modelIdx); + QCOMPARE(tma.data(tmaIdx, Qt::DisplayRole).toString(), model.displayData(modelIdx)); + QCOMPARE(tma.data(tmaIdx, QQuickTreeModelAdaptor::DepthRole).toInt(), model.level(modelIdx)); + QCOMPARE(tma.data(tmaIdx, QQuickTreeModelAdaptor::ExpandedRole).toBool(), expanded); + QCOMPARE(tma.data(tmaIdx, QQuickTreeModelAdaptor::HasChildrenRole).toBool(), model.hasChildren(modelIdx)); +} + +void tst_QQuickTreeModelAdaptor::expandAndTest(const QModelIndex &idx, QQuickTreeModelAdaptor &tma, bool expandable, + int expectedRowCountDifference) +{ + QSignalSpy rowsAboutToBeInsertedSpy(&tma, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int))); + QSignalSpy rowsInsertedSpy(&tma, SIGNAL(rowsInserted(QModelIndex,int,int))); + QSignalSpy dataChangedSpy(&tma, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>))); + + int oldRowCount = tma.rowCount(); + tma.expand(idx); + QCOMPARE(tma.isExpanded(idx), expandable); + + const QModelIndex &tmaIdx = tma.index(tma.itemIndex(idx)); + QCOMPARE(tma.data(tmaIdx, QQuickTreeModelAdaptor::ExpandedRole).toBool(), expandable); + + if (expandable) { + // Rows were added below the parent + QCOMPARE(tma.rowCount(), oldRowCount + expectedRowCountDifference); + QCOMPARE(rowsAboutToBeInsertedSpy.count(), rowsInsertedSpy.count()); + QVERIFY(rowsInsertedSpy.count() > 0); + if (rowsInsertedSpy.count() == 1) { + const QVariantList &rowsAboutToBeInsertedArgs = rowsAboutToBeInsertedSpy.takeFirst(); + const QVariantList &rowsInsertedArgs = rowsInsertedSpy.takeFirst(); + for (int i = 0; i < rowsInsertedArgs.count(); i++) + QCOMPARE(rowsAboutToBeInsertedArgs.at(i), rowsInsertedArgs.at(i)); + QCOMPARE(rowsInsertedArgs.at(0).toModelIndex(), QModelIndex()); + QCOMPARE(rowsInsertedArgs.at(1).toInt(), tma.itemIndex(idx) + 1); + QCOMPARE(rowsInsertedArgs.at(2).toInt(), tma.itemIndex(idx) + expectedRowCountDifference); + } + + // Data changed for the parent's ExpandedRole (value checked above) + QCOMPARE(dataChangedSpy.count(), 1); + const QVariantList &dataChangedArgs = dataChangedSpy.first(); + QCOMPARE(dataChangedArgs.at(0).toModelIndex(), tmaIdx); + QCOMPARE(dataChangedArgs.at(1).toModelIndex(), tmaIdx); + QCOMPARE(dataChangedArgs.at(2).value<QVector<int> >(), QVector<int>(1, QQuickTreeModelAdaptor::ExpandedRole)); + } else { + QCOMPARE(tma.rowCount(), oldRowCount); + QCOMPARE(rowsAboutToBeInsertedSpy.count(), 0); + QCOMPARE(rowsInsertedSpy.count(), 0); + } +} + +void tst_QQuickTreeModelAdaptor::collapseAndTest(const QModelIndex &idx, QQuickTreeModelAdaptor &tma, + bool expandable, int expectedRowCountDifference) +{ + QSignalSpy rowsAboutToBeRemovedSpy(&tma, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int))); + QSignalSpy rowsRemovedSpy(&tma, SIGNAL(rowsRemoved(QModelIndex,int,int))); + QSignalSpy dataChangedSpy(&tma, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>))); + + int oldRowCount = tma.rowCount(); + tma.collapse(idx); + QVERIFY(!tma.isExpanded(idx)); + + const QModelIndex &tmaIdx = tma.index(tma.itemIndex(idx)); + if (tmaIdx.isValid()) + QCOMPARE(tma.data(tmaIdx, QQuickTreeModelAdaptor::ExpandedRole).toBool(), false); + + if (expandable) { + // Rows were removed below the parent + QCOMPARE(tma.rowCount(), oldRowCount - expectedRowCountDifference); + QCOMPARE(rowsAboutToBeRemovedSpy.count(), 1); + QCOMPARE(rowsRemovedSpy.count(), 1); + const QVariantList &rowsAboutToBeRemovedArgs = rowsAboutToBeRemovedSpy.takeFirst(); + const QVariantList &rowsRemovedArgs = rowsRemovedSpy.takeFirst(); + for (int i = 0; i < rowsRemovedArgs.count(); i++) + QCOMPARE(rowsAboutToBeRemovedArgs.at(i), rowsRemovedArgs.at(i)); + QCOMPARE(rowsRemovedArgs.at(0).toModelIndex(), QModelIndex()); + QCOMPARE(rowsRemovedArgs.at(1).toInt(), tma.itemIndex(idx) + 1); + QCOMPARE(rowsRemovedArgs.at(2).toInt(), tma.itemIndex(idx) + expectedRowCountDifference); + + // Data changed for the parent's ExpandedRole (value checked above) + QCOMPARE(dataChangedSpy.count(), 1); + const QVariantList &dataChangedArgs = dataChangedSpy.first(); + QCOMPARE(dataChangedArgs.at(0).toModelIndex(), tmaIdx); + QCOMPARE(dataChangedArgs.at(1).toModelIndex(), tmaIdx); + QCOMPARE(dataChangedArgs.at(2).value<QVector<int> >(), QVector<int>(1, QQuickTreeModelAdaptor::ExpandedRole)); + } else { + QCOMPARE(tma.rowCount(), oldRowCount); + QCOMPARE(rowsAboutToBeRemovedSpy.count(), 0); + QCOMPARE(rowsRemovedSpy.count(), 0); + } +} + +void tst_QQuickTreeModelAdaptor::compareModels(QQuickTreeModelAdaptor &tma, TestModel &model) +{ + QModelIndex parent; + QStack<QModelIndex> parents; + QModelIndex idx = model.index(0, 0); + int modelVisibleRows = model.rowCount(parent); + for (int i = 0; i < tma.rowCount(); i++) { + bool expanded = tma.isExpanded(i); + compareData(i, tma, idx, model, expanded); + if (expanded) { + parents.push(parent); + parent = idx; + modelVisibleRows += model.rowCount(parent); + idx = model.index(0, 0, parent); + } else { + while (idx.row() == model.rowCount(parent) - 1) { + if (parents.isEmpty()) + break; + idx = parent; + parent = parents.pop(); + } + idx = model.index(idx.row() + 1, 0, parent); + } + } + QCOMPARE(tma.rowCount(), modelVisibleRows); + + // Duplicates the model inspection above, but provides extra tests + QVERIFY(tma.testConsistency()); +} + +void tst_QQuickTreeModelAdaptor::setModel() +{ + TestModel model(5, 1); + QQuickTreeModelAdaptor tma; + + QSignalSpy modelChangedSpy(&tma, SIGNAL(modelChanged(QAbstractItemModel*))); + tma.setModel(&model); + QCOMPARE(modelChangedSpy.count(), 1); + QCOMPARE(tma.model(), &model); + + // Set same model twice + tma.setModel(&model); + QCOMPARE(modelChangedSpy.count(), 1); + + modelChangedSpy.clear(); + tma.setModel(0); + QCOMPARE(modelChangedSpy.count(), 1); + QCOMPARE(tma.model(), static_cast<QAbstractItemModel *>(0)); +} + +void tst_QQuickTreeModelAdaptor::modelReset() +{ + TestModel model(5, 1); + QQuickTreeModelAdaptor tma; + tma.setModel(&model); + + QSignalSpy modelAboutToBeResetSpy(&tma, SIGNAL(modelAboutToBeReset())); + QSignalSpy modelResetSpy(&tma, SIGNAL(modelReset())); + + // Nothing expanded + model.resetModel(); + QCOMPARE(modelAboutToBeResetSpy.count(), 1); + QCOMPARE(modelResetSpy.count(), 1); + QCOMPARE(tma.rowCount(), model.rowCount()); + compareModels(tma, model); + + // Expanded items should not be anymore + tma.expand(model.index(0, 0)); + tma.expand(model.index(2, 0)); + tma.expand(model.index(2, 0, model.index(2, 0))); + modelAboutToBeResetSpy.clear(); + modelResetSpy.clear(); + model.resetModel(); + QCOMPARE(modelAboutToBeResetSpy.count(), 1); + QCOMPARE(modelResetSpy.count(), 1); + QCOMPARE(tma.rowCount(), model.rowCount()); + compareModels(tma, model); +} + +void tst_QQuickTreeModelAdaptor::dataAccess() +{ + TestModel model(5, 1); + + QQuickTreeModelAdaptor tma; + tma.setModel(&model); + + QCOMPARE(tma.rowCount(), model.rowCount()); + compareModels(tma, model); + + QModelIndex parentIdx = model.index(2, 0); + QVERIFY(model.hasChildren(parentIdx)); + tma.expand(parentIdx); + QVERIFY(tma.isExpanded(parentIdx)); + QCOMPARE(tma.rowCount(), model.rowCount() + model.rowCount(parentIdx)); + compareModels(tma, model); + + tma.collapse(parentIdx); + QCOMPARE(tma.rowCount(), model.rowCount()); + compareModels(tma, model); +} + +void tst_QQuickTreeModelAdaptor::dataChange() +{ + TestModel model(5, 1); + + QQuickTreeModelAdaptor tma; + tma.setModel(&model); + + QSignalSpy dataChangedSpy(&tma, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>))); + const QModelIndex &idx = model.index(2, 0); + model.setData(idx, QVariant(), Qt::DisplayRole); + QCOMPARE(dataChangedSpy.count(), 1); + const QVariantList &dataChangedArgs = dataChangedSpy.first(); + const QModelIndex &tmaIdx = tma.index(tma.itemIndex(idx)); + QCOMPARE(dataChangedArgs.at(0).toModelIndex(), tmaIdx); + QCOMPARE(dataChangedArgs.at(1).toModelIndex(), tmaIdx); + QCOMPARE(dataChangedArgs.at(2).value<QVector<int> >(), QVector<int>(1, Qt::DisplayRole)); + compareModels(tma, model); + + { + // Non expanded children shouldn't emit any signal + dataChangedSpy.clear(); + const QModelIndex &childIdx = model.index(4, 0, idx); + model.setData(childIdx, QVariant(), Qt::DisplayRole); + QCOMPARE(dataChangedSpy.count(), 0); + compareModels(tma, model); + + // But expanded children should + tma.expand(idx); + QVERIFY(tma.isExpanded(idx)); + dataChangedSpy.clear(); // expand() emits dataChanged() with ExpandedRole + model.setData(childIdx, QVariant(), Qt::DisplayRole); + QCOMPARE(dataChangedSpy.count(), 1); + const QVariantList &dataChangedArgs = dataChangedSpy.first(); + const QModelIndex &tmaIdx = tma.index(tma.itemIndex(childIdx)); + QCOMPARE(dataChangedArgs.at(0).toModelIndex(), tmaIdx); + QCOMPARE(dataChangedArgs.at(1).toModelIndex(), tmaIdx); + QCOMPARE(dataChangedArgs.at(2).value<QVector<int> >(), QVector<int>(1, Qt::DisplayRole)); + compareModels(tma, model); + } +} + +void tst_QQuickTreeModelAdaptor::groupedDataChange() +{ + TestModel model(10, 1); + const QModelIndex &topLeftIdx = model.index(1, 0); + const QModelIndex &bottomRightIdx = model.index(7, 0); + + QQuickTreeModelAdaptor tma; + tma.setModel(&model); + + QSignalSpy dataChangedSpy(&tma, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>))); + const QVector<int> roles(1, Qt::DisplayRole); + + { + // No expanded items + model.groupedSetData(topLeftIdx, bottomRightIdx, roles); + QCOMPARE(dataChangedSpy.count(), 1); + compareModels(tma, model); + + const QModelIndex &tmaTLIdx = tma.index(tma.itemIndex(topLeftIdx)); + const QModelIndex &tmaBRIdx = tma.index(tma.itemIndex(bottomRightIdx)); + const QVariantList &dataChangedArgs = dataChangedSpy.first(); + QCOMPARE(dataChangedArgs.at(0).toModelIndex(), tmaTLIdx); + QCOMPARE(dataChangedArgs.at(1).toModelIndex(), tmaBRIdx); + QCOMPARE(dataChangedArgs.at(2).value<QVector<int> >(), roles); + } + + // One item expanded in the group range + const QModelIndex &expandedIdx = model.index(4, 0); + tma.expand(expandedIdx); + QVERIFY(tma.isExpanded(expandedIdx)); + + for (int i = 0; i < 2; i++) { + const QModelIndex &tmaTLIdx = tma.index(tma.itemIndex(topLeftIdx)); + const QModelIndex &tmaExpandedIdx = tma.index(tma.itemIndex(expandedIdx)); + const QModelIndex &tmaExpandedSiblingIdx = tma.index(tma.itemIndex(expandedIdx.sibling(expandedIdx.row() + 1, 0))); + const QModelIndex &tmaBRIdx = tma.index(tma.itemIndex(bottomRightIdx)); + + dataChangedSpy.clear(); // expand() sends a dataChaned() signal + model.groupedSetData(topLeftIdx, bottomRightIdx, roles); + QCOMPARE(dataChangedSpy.count(), 2); + compareModels(tma, model); + + QVariantList dataChangedArgs = dataChangedSpy.takeFirst(); + QCOMPARE(dataChangedArgs.at(0).toModelIndex(), tmaTLIdx); + QCOMPARE(dataChangedArgs.at(1).toModelIndex(), tmaExpandedIdx); + QCOMPARE(dataChangedArgs.at(2).value<QVector<int> >(), roles); + + dataChangedArgs = dataChangedSpy.takeFirst(); + QCOMPARE(dataChangedArgs.at(0).toModelIndex(), tmaExpandedSiblingIdx); + QCOMPARE(dataChangedArgs.at(1).toModelIndex(), tmaBRIdx); + QCOMPARE(dataChangedArgs.at(2).value<QVector<int> >(), roles); + + // Further expanded descendants should not change grouping + tma.expand(model.index(0, 0, expandedIdx)); + QVERIFY(tma.isExpanded(expandedIdx)); + } + tma.collapse(model.index(0, 0, expandedIdx)); + + // Let's expand one more and see what happens... + const QModelIndex &otherExpandedIdx = model.index(6, 0); + tma.expand(otherExpandedIdx); + QVERIFY(tma.isExpanded(otherExpandedIdx)); + + for (int i = 0; i < 3; i++) { + const QModelIndex &tmaTLIdx = tma.index(tma.itemIndex(topLeftIdx)); + const QModelIndex &tmaExpandedIdx = tma.index(tma.itemIndex(expandedIdx)); + const QModelIndex &tmaExpandedSiblingIdx = tma.index(tma.itemIndex(expandedIdx.sibling(expandedIdx.row() + 1, 0))); + const QModelIndex &tmaOtherExpandedIdx = tma.index(tma.itemIndex(otherExpandedIdx)); + const QModelIndex &tmaOtherExpandedSiblingIdx = tma.index(tma.itemIndex(otherExpandedIdx.sibling(otherExpandedIdx.row() + 1, 0))); + const QModelIndex &tmaBRIdx = tma.index(tma.itemIndex(bottomRightIdx)); + + dataChangedSpy.clear(); // expand() sends a dataChaned() signal + model.groupedSetData(topLeftIdx, bottomRightIdx, roles); + QCOMPARE(dataChangedSpy.count(), 3); + compareModels(tma, model); + + QVariantList dataChangedArgs = dataChangedSpy.takeFirst(); + QCOMPARE(dataChangedArgs.at(0).toModelIndex(), tmaTLIdx); + QCOMPARE(dataChangedArgs.at(1).toModelIndex(), tmaExpandedIdx); + QCOMPARE(dataChangedArgs.at(2).value<QVector<int> >(), roles); + + dataChangedArgs = dataChangedSpy.takeFirst(); + QCOMPARE(dataChangedArgs.at(0).toModelIndex(), tmaExpandedSiblingIdx); + QCOMPARE(dataChangedArgs.at(1).toModelIndex(), tmaOtherExpandedIdx); + QCOMPARE(dataChangedArgs.at(2).value<QVector<int> >(), roles); + + dataChangedArgs = dataChangedSpy.takeFirst(); + QCOMPARE(dataChangedArgs.at(0).toModelIndex(), tmaOtherExpandedSiblingIdx); + QCOMPARE(dataChangedArgs.at(1).toModelIndex(), tmaBRIdx); + QCOMPARE(dataChangedArgs.at(2).value<QVector<int> >(), roles); + + // Further expanded descendants should not change grouping + if (i == 0) { + tma.expand(model.index(0, 0, expandedIdx)); + QVERIFY(tma.isExpanded(expandedIdx)); + } else { + tma.expand(model.index(0, 0, otherExpandedIdx)); + QVERIFY(tma.isExpanded(expandedIdx)); + } + } +} + +void tst_QQuickTreeModelAdaptor::expandAndCollapse_data() +{ + QTest::addColumn<int>("parentRow"); + QTest::newRow("First") << 0; + QTest::newRow("Middle") << 2; + QTest::newRow("Last") << 4; + QTest::newRow("Non expandable") << 3; +} + +void tst_QQuickTreeModelAdaptor::expandAndCollapse() +{ + QFETCH(int, parentRow); + TestModel model(5, 1); + const QModelIndex &parentIdx = model.index(parentRow, 0); + bool expandable = model.hasChildren(parentIdx); + + QQuickTreeModelAdaptor tma; + tma.setModel(&model); + + expandAndTest(parentIdx, tma, expandable, model.rowCount(parentIdx)); + compareModels(tma, model); + + collapseAndTest(parentIdx, tma, expandable, model.rowCount(parentIdx)); + compareModels(tma, model); +} + +void tst_QQuickTreeModelAdaptor::expandAndCollapse2ndLevel() +{ + const int expandRows[] = { 0, 2, 4, 3 }; + const int expandRowsCount = sizeof(expandRows) / sizeof(expandRows[0]); + for (int i = 0; i < expandRowsCount - 1; i++) { // Skip last non-expandable row + TestModel model(5, 1); + const QModelIndex &parentIdx = model.index(expandRows[i], 0); + QVERIFY(model.hasChildren(parentIdx)); + + QQuickTreeModelAdaptor tma; + tma.setModel(&model); + + tma.expand(parentIdx); + QVERIFY(tma.isExpanded(parentIdx)); + QCOMPARE(tma.rowCount(), model.rowCount() + model.rowCount(parentIdx)); + + for (int j = 0; j < expandRowsCount; j++) { + const QModelIndex &childIdx = model.index(expandRows[j], 0, parentIdx); + bool expandable = model.hasChildren(childIdx); + + // Expand child + expandAndTest(childIdx, tma, expandable, model.rowCount(childIdx)); + compareModels(tma, model); + // Collapse child + collapseAndTest(childIdx, tma, expandable, model.rowCount(childIdx)); + compareModels(tma, model); + + // Expand child again + expandAndTest(childIdx, tma, expandable, model.rowCount(childIdx)); + compareModels(tma, model); + // Collapse parent -> child node invisible, but expanded + collapseAndTest(parentIdx, tma, true, model.rowCount(parentIdx) + model.rowCount(childIdx)); + compareModels(tma, model); + QCOMPARE(tma.isExpanded(childIdx), expandable); + // Expand parent again + expandAndTest(parentIdx, tma, true, model.rowCount(parentIdx) + model.rowCount(childIdx)); + compareModels(tma, model); + + // Collapse parent -> child node invisible, but expanded + collapseAndTest(parentIdx, tma, true, model.rowCount(parentIdx) + model.rowCount(childIdx)); + compareModels(tma, model); + QCOMPARE(tma.isExpanded(childIdx), expandable); + // Collapse child -> nothing should change + collapseAndTest(childIdx, tma, false, 0); + compareModels(tma, model); + // Expand parent again + expandAndTest(parentIdx, tma, true, model.rowCount(parentIdx)); + compareModels(tma, model); + + // Expand child, one last time + expandAndTest(childIdx, tma, expandable, model.rowCount(childIdx)); + compareModels(tma, model); + // Collapse child, and done + collapseAndTest(childIdx, tma, expandable, model.rowCount(childIdx)); + compareModels(tma, model); + } + } +} + +void tst_QQuickTreeModelAdaptor::layoutChange() +{ + TestModel model(5, 1); + const QModelIndex &idx = model.index(0, 0); + const QModelIndex &idx2 = model.index(2, 0); + + QQuickTreeModelAdaptor tma; + tma.setModel(&model); + + // Nothing expanded + QSignalSpy dataChangedSpy(&tma, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>))); + model.changeLayout(); + QCOMPARE(dataChangedSpy.count(), 1); + QVariantList dataChangedArgs = dataChangedSpy.takeFirst(); + QCOMPARE(dataChangedArgs.at(0).toModelIndex(), tma.index(0)); + QCOMPARE(dataChangedArgs.at(1).toModelIndex(), tma.index(tma.rowCount() - 1)); + QVERIFY(dataChangedArgs.at(2).value<QVector<int> >().isEmpty()); + compareModels(tma, model); + + // One item expanded + tma.expand(idx); + QVERIFY(tma.isExpanded(idx)); + dataChangedSpy.clear(); + model.changeLayout(); + QCOMPARE(dataChangedSpy.count(), 1); + dataChangedArgs = dataChangedSpy.takeFirst(); + QCOMPARE(dataChangedArgs.at(0).toModelIndex(), tma.index(0)); + QCOMPARE(dataChangedArgs.at(1).toModelIndex(), tma.index(tma.rowCount() - 1)); + QVERIFY(dataChangedArgs.at(2).value<QVector<int> >().isEmpty()); + compareModels(tma, model); + + // One parent layout change, expanded + dataChangedSpy.clear(); + QList<QPersistentModelIndex> parents; + parents << idx; + model.changeLayout(parents); + QCOMPARE(dataChangedSpy.count(), 1); + dataChangedArgs = dataChangedSpy.takeFirst(); + QCOMPARE(dataChangedArgs.at(0).toModelIndex(), tma.index(tma.itemIndex(model.index(0, 0, idx)))); + QCOMPARE(dataChangedArgs.at(1).toModelIndex(), tma.index(tma.itemIndex(model.index(model.rowCount(idx) - 1, 0, idx)))); + QVERIFY(dataChangedArgs.at(2).value<QVector<int> >().isEmpty()); + compareModels(tma, model); + + // One parent layout change, collapsed + tma.collapse(idx); + dataChangedSpy.clear(); + model.changeLayout(parents); + QCOMPARE(dataChangedSpy.count(), 0); + compareModels(tma, model); + + // Two-parent layout change, both collapsed + parents << idx2; + dataChangedSpy.clear(); + model.changeLayout(parents); + QCOMPARE(dataChangedSpy.count(), 0); + compareModels(tma, model); + + // Two-parent layout change, only one expanded + tma.expand(idx2); + QVERIFY(tma.isExpanded(idx2)); + dataChangedSpy.clear(); + model.changeLayout(parents); + QCOMPARE(dataChangedSpy.count(), 1); + dataChangedArgs = dataChangedSpy.takeFirst(); + QCOMPARE(dataChangedArgs.at(0).toModelIndex(), tma.index(tma.itemIndex(model.index(0, 0, idx2)))); + QCOMPARE(dataChangedArgs.at(1).toModelIndex(), tma.index(tma.itemIndex(model.index(model.rowCount(idx2) - 1, 0, idx2)))); + QVERIFY(dataChangedArgs.at(2).value<QVector<int> >().isEmpty()); + compareModels(tma, model); + + // Two-parent layout change, both expanded + tma.expand(idx); + QVERIFY(tma.isExpanded(idx)); + dataChangedSpy.clear(); + model.changeLayout(parents); + QCOMPARE(dataChangedSpy.count(), 2); + dataChangedArgs = dataChangedSpy.takeFirst(); + QCOMPARE(dataChangedArgs.at(0).toModelIndex(), tma.index(tma.itemIndex(model.index(0, 0, idx)))); + QCOMPARE(dataChangedArgs.at(1).toModelIndex(), tma.index(tma.itemIndex(model.index(model.rowCount(idx) - 1, 0, idx)))); + QVERIFY(dataChangedArgs.at(2).value<QVector<int> >().isEmpty()); + dataChangedArgs = dataChangedSpy.takeFirst(); + QCOMPARE(dataChangedArgs.at(0).toModelIndex(), tma.index(tma.itemIndex(model.index(0, 0, idx2)))); + QCOMPARE(dataChangedArgs.at(1).toModelIndex(), tma.index(tma.itemIndex(model.index(model.rowCount(idx2) - 1, 0, idx2)))); + QVERIFY(dataChangedArgs.at(2).value<QVector<int> >().isEmpty()); + compareModels(tma, model); +} + +static const int ModelRowCount = 9; + +void tst_QQuickTreeModelAdaptor::removeRows_data() +{ + QTest::addColumn<int>("removeFromRow"); + QTest::addColumn<int>("removeCount"); + QTest::addColumn<int>("removeParentRow"); + QTest::addColumn<int>("expandRow"); + QTest::addColumn<int>("expandParentRow"); + QTest::addColumn<int>("expectedRemovedCount"); + + QTest::newRow("Nothing expanded, remove 1st row") << 0 << 1 << -1 << -1 << -1 << 1; + QTest::newRow("Expand 1st row, remove 1st row") << 0 << 1 << -1 << 0 << -1 << 1 + ModelRowCount; + QTest::newRow("Expand last row, remove 1st row") << 0 << 1 << -1 << ModelRowCount - 1 << -1 << 1; + QTest::newRow("Nothing expanded, remove last row") << ModelRowCount - 1 << 1 << -1 << -1 << -1 << 1; + QTest::newRow("Expand 1st row, remove last row") << ModelRowCount - 1 << 1 << -1 << 0 << -1 << 1; + QTest::newRow("Expand last row, remove last row") << ModelRowCount - 1 << 1 << -1 << ModelRowCount - 1 << -1 << 1 + ModelRowCount; + QTest::newRow("Remove child row, parent collapsed") << 2 << 1 << 0 << -1 << -1 << 0; + QTest::newRow("Remove child row, parent expanded") << 2 << 1 << 0 << 0 << -1 << 1; + QTest::newRow("Remove several rows, nothing expanded") << 2 << 5 << -1 << -1 << -1 << 5; + QTest::newRow("Remove several rows, 1st row expanded") << 2 << 5 << -1 << 0 << -1 << 5; + QTest::newRow("Remove several rows, last row expanded") << 2 << 5 << -1 << ModelRowCount - 1 << -1 << 5; + QTest::newRow("Remove several rows, one of them expanded") << 2 << 5 << -1 << 4 << -1 << 5 + ModelRowCount; + QTest::newRow("Remove all rows, nothing expanded") << 0 << ModelRowCount << -1 << -1 << -1 << ModelRowCount; + QTest::newRow("Remove all rows, 1st row expanded") << 0 << ModelRowCount << -1 << 0 << -1 << ModelRowCount * 2; + QTest::newRow("Remove all rows, last row expanded") << 0 << ModelRowCount << -1 << ModelRowCount - 1 << -1 << ModelRowCount * 2; + QTest::newRow("Remove all rows, random one expanded") << 0 << ModelRowCount << -1 << 4 << -1 << ModelRowCount * 2; +} + +void tst_QQuickTreeModelAdaptor::removeRows() +{ + QFETCH(int, removeFromRow); + QFETCH(int, removeCount); + QFETCH(int, removeParentRow); + QFETCH(int, expandRow); + QFETCH(int, expandParentRow); + QFETCH(int, expectedRemovedCount); + + TestModel model(ModelRowCount, 1); + QQuickTreeModelAdaptor tma; + tma.setModel(&model); + + const QModelIndex &expandParentIdx = expandParentRow == -1 ? QModelIndex() : model.index(expandParentRow, 0); + if (expandParentIdx.isValid()) { + tma.expand(expandParentIdx); + QVERIFY(tma.isExpanded(expandParentIdx)); + } + const QModelIndex &expandIdx = model.index(expandRow, 0, expandParentIdx); + if (expandIdx.isValid()) { + tma.expand(expandIdx); + QVERIFY(tma.isExpanded(expandIdx)); + } + + const QModelIndex &removeParentIdx = removeParentRow == -1 ? QModelIndex() : model.index(removeParentRow, 0); + const QModelIndex &removeIdx = model.index(removeFromRow, 0, removeParentIdx); + int tmaItemIdx = tma.itemIndex(removeIdx); + + QSignalSpy rowsAboutToBeRemovedSpy(&tma, SIGNAL(rowsAboutToBeRemoved(const QModelIndex&, int, int))); + QSignalSpy rowsRemovedSpy(&tma, SIGNAL(rowsRemoved(const QModelIndex&, int, int))); + model.removeRows(removeFromRow, removeCount, removeParentIdx); + if (expectedRemovedCount == 0) { + QCOMPARE(rowsAboutToBeRemovedSpy.count(), 0); + QCOMPARE(rowsRemovedSpy.count(), 0); + } else { + QCOMPARE(rowsAboutToBeRemovedSpy.count(), 1); + QCOMPARE(rowsRemovedSpy.count(), 1); + QVariantList rowsAboutToBeRemovedArgs = rowsAboutToBeRemovedSpy.first(); + QVariantList rowsRemovedArgs = rowsRemovedSpy.first(); + QCOMPARE(rowsAboutToBeRemovedArgs, rowsRemovedArgs); + QCOMPARE(rowsAboutToBeRemovedArgs.at(0).toModelIndex(), QModelIndex()); + QCOMPARE(rowsAboutToBeRemovedArgs.at(1).toInt(), tmaItemIdx); + QCOMPARE(rowsAboutToBeRemovedArgs.at(2).toInt(), tmaItemIdx + expectedRemovedCount - 1); + } +} + +void tst_QQuickTreeModelAdaptor::insertRows_data() +{ + QTest::addColumn<int>("insertFromRow"); + QTest::addColumn<int>("insertCount"); + QTest::addColumn<int>("insertParentRow"); + QTest::addColumn<int>("expandRow"); + QTest::addColumn<int>("expandParentRow"); + QTest::addColumn<int>("expectedInsertedCount"); + + QTest::newRow("Nothing expanded, insert 1st row") << 0 << 1 << -1 << -1 << -1 << 1; + QTest::newRow("Expand 1st row, insert 1st row") << 0 << 1 << -1 << 0 << -1 << 1; + QTest::newRow("Expand last row, insert 1st row") << 0 << 1 << -1 << ModelRowCount - 1 << -1 << 1; + QTest::newRow("Nothing expanded, insert before the last row") << ModelRowCount - 1 << 1 << -1 << -1 << -1 << 1; + QTest::newRow("Nothing expanded, insert after the last row") << ModelRowCount << 1 << -1 << -1 << -1 << 1; + QTest::newRow("Expand 1st row, insert before the last row") << ModelRowCount - 1 << 1 << -1 << 0 << -1 << 1; + QTest::newRow("Expand 1st row, insert after the last row") << ModelRowCount << 1 << -1 << 0 << -1 << 1; + QTest::newRow("Expand last row, insert before the last row") << ModelRowCount - 1 << 1 << -1 << ModelRowCount - 1 << -1 << 1; + QTest::newRow("Expand last row, insert after the last row") << ModelRowCount << 1 << -1 << ModelRowCount - 1 << -1 << 1; + QTest::newRow("Insert child row, parent collapsed") << 2 << 1 << 0 << -1 << -1 << 0; + QTest::newRow("Insert child row, parent expanded") << 2 << 1 << 0 << 0 << -1 << 1; + QTest::newRow("Insert several rows, nothing expanded") << 2 << 5 << -1 << -1 << -1 << 5; + QTest::newRow("Insert several rows, 1st row expanded") << 2 << 5 << -1 << 0 << -1 << 5; + QTest::newRow("Insert several rows, last row expanded") << 2 << 5 << -1 << ModelRowCount - 1 << -1 << 5; +} + +void tst_QQuickTreeModelAdaptor::insertRows() +{ + QFETCH(int, insertFromRow); + QFETCH(int, insertCount); + QFETCH(int, insertParentRow); + QFETCH(int, expandRow); + QFETCH(int, expandParentRow); + QFETCH(int, expectedInsertedCount); + + TestModel model(ModelRowCount, 1); + QQuickTreeModelAdaptor tma; + tma.setModel(&model); + + const QModelIndex &expandParentIdx = expandParentRow == -1 ? QModelIndex() : model.index(expandParentRow, 0); + if (expandParentIdx.isValid()) { + tma.expand(expandParentIdx); + QVERIFY(tma.isExpanded(expandParentIdx)); + } + const QModelIndex &expandIdx = model.index(expandRow, 0, expandParentIdx); + if (expandIdx.isValid()) { + tma.expand(expandIdx); + QVERIFY(tma.isExpanded(expandIdx)); + } + + const QModelIndex &insertParentIdx = insertParentRow == -1 ? QModelIndex() : model.index(insertParentRow, 0); + const QModelIndex &insertIdx = model.index(insertFromRow, 0, insertParentIdx); + int tmaItemIdx = insertFromRow == model.rowCount(insertParentIdx) ? tma.rowCount() : tma.itemIndex(insertIdx); + + QSignalSpy rowsAboutToBeInsertedSpy(&tma, SIGNAL(rowsAboutToBeInserted(const QModelIndex&, int, int))); + QSignalSpy rowsInsertedSpy(&tma, SIGNAL(rowsInserted(const QModelIndex&, int, int))); + model.insertRows(insertFromRow, insertCount, insertParentIdx); + if (expectedInsertedCount == 0) { + QCOMPARE(rowsAboutToBeInsertedSpy.count(), 0); + QCOMPARE(rowsInsertedSpy.count(), 0); + } else { + QCOMPARE(rowsAboutToBeInsertedSpy.count(), 1); + QCOMPARE(rowsInsertedSpy.count(), 1); + QVariantList rowsAboutToBeInsertedArgs = rowsAboutToBeInsertedSpy.first(); + QVariantList rowsInsertedArgs = rowsInsertedSpy.first(); + QCOMPARE(rowsAboutToBeInsertedArgs, rowsInsertedArgs); + QCOMPARE(rowsAboutToBeInsertedArgs.at(0).toModelIndex(), QModelIndex()); + QCOMPARE(rowsAboutToBeInsertedArgs.at(1).toInt(), tmaItemIdx); + QCOMPARE(rowsAboutToBeInsertedArgs.at(2).toInt(), tmaItemIdx + expectedInsertedCount - 1); + QCOMPARE(tma.itemIndex(model.index(insertFromRow, 0, insertParentIdx)), tmaItemIdx); + } +} + +enum MoveSignalType { + RowsMoved = 0, RowsInserted, RowsRemoved +}; + +void tst_QQuickTreeModelAdaptor::moveRows_data() +{ + QTest::addColumn<int>("sourceRow"); + QTest::addColumn<bool>("expandSource"); + QTest::addColumn<int>("moveCount"); + QTest::addColumn<int>("sourceParentRow"); + QTest::addColumn<bool>("expandSourceParent"); + QTest::addColumn<int>("destRow"); + QTest::addColumn<bool>("expandDest"); + QTest::addColumn<int>("destParentRow"); + QTest::addColumn<bool>("expandDestParent"); + QTest::addColumn<int>("expandRow"); + QTest::addColumn<int>("expandParentRow"); + QTest::addColumn<int>("signalType"); + QTest::addColumn<int>("expectedMovedCount"); + + QTest::newRow("From and to top-level parent") + << 0 << false << 1 << -1 << false + << 3 << false << -1 << false + << -1 << -1 << (int)RowsMoved << 1; + QTest::newRow("From and to top-level parent, expanded") + << 0 << true << 1 << -1 << false + << 3 << false << -1 << false + << -1 << -1 << (int)RowsMoved << ModelRowCount + 1; + QTest::newRow("From and to top-level parent, backwards") + << 4 << false << 1 << -1 << false + << 0 << false << -1 << false + << -1 << -1 << (int)RowsMoved << 1; + QTest::newRow("From and to top-level parent, expanded, backwards") + << 4 << true << 1 << -1 << false + << 0 << false << -1 << false + << -1 << -1 << (int)RowsMoved << ModelRowCount + 1; + QTest::newRow("Moving between collapsed parents") + << 0 << false << 1 << 0 << false + << 0 << false << 2 << false + << -1 << -1 << (int)RowsMoved << 0; + QTest::newRow("From expanded parent to collapsed parent") + << 0 << false << 1 << 0 << true + << 0 << false << 2 << false + << -1 << -1 << (int)RowsRemoved << 1; + QTest::newRow("From collapsed parent to expanded parent") + << 0 << false << 1 << 0 << false + << 0 << false << 2 << true + << -1 << -1 << (int)RowsInserted << 1; + QTest::newRow("From and to same expanded parent") + << 0 << false << 1 << 0 << true + << 2 << false << 0 << false + << -1 << -1 << (int)RowsMoved << 1; + QTest::newRow("From expanded parent to collapsed parent, expanded row") + << 0 << true << 1 << 0 << true + << 0 << false << 2 << false + << -1 << -1 << (int)RowsRemoved << ModelRowCount + 1; + QTest::newRow("From collapsed parent to expanded parent, expanded row") + << 0 << true << 1 << 0 << false + << 0 << false << 2 << true + << -1 << -1 << (int)RowsInserted << ModelRowCount + 1; + QTest::newRow("From and to same expanded parent, expanded row, forward") + << 0 << true << 1 << 0 << true + << 5 << false << 0 << false + << -1 << -1 << (int)RowsMoved << ModelRowCount + 1; + QTest::newRow("From and to same expanded parent, expanded row, last row") + << 0 << true << 1 << 0 << true + << ModelRowCount << false << 0 << false + << -1 << -1 << (int)RowsMoved << ModelRowCount + 1; + QTest::newRow("From and to same expanded parent, expanded row, several") + << 0 << true << 3 << 0 << true + << 5 << false << 0 << false + << -1 << -1 << (int)RowsMoved << ModelRowCount + 3; + QTest::newRow("From and to same expanded parent, expanded row, backward") + << 6 << true << 1 << 0 << true + << 0 << false << 0 << false + << -1 << -1 << (int)RowsMoved << ModelRowCount + 1; + QTest::newRow("From and to same expanded parent, expanded row, several, backward") + << 6 << true << 2 << 0 << true + << 0 << false << 0 << false + << -1 << -1 << (int)RowsMoved << ModelRowCount + 2; + QTest::newRow("From and to different expanded parents") + << 0 << false << 1 << 0 << true + << 1 << false << 4 << true + << -1 << -1 << (int)RowsMoved << 1; + QTest::newRow("From and to different expanded parents, backward") + << 0 << false << 1 << 4 << true + << 2 << false << 0 << true + << -1 << -1 << (int)RowsMoved << 1; + QTest::newRow("From and to different expanded parents, up in level") + << 0 << false << 1 << 0 << true + << 5 << true << -1 << true + << -1 << -1 << (int)RowsMoved << 1; + QTest::newRow("From and to different expanded parents, up in level, backwards") + << 0 << false << 1 << 4 << true + << 1 << false << -1 << true + << -1 << -1 << (int)RowsMoved << 1; + QTest::newRow("From and to different expanded parents, up in level, as 1st item") + << 0 << false << 1 << 0 << true + << 0 << false << -1 << true + << -1 << -1 << (int)RowsMoved << 1; + QTest::newRow("From and to different expanded parents, backward, up in level") + << 0 << false << 1 << 4 << true + << 2 << false << 0 << true + << -1 << -1 << (int)RowsMoved << 1; +} + +void tst_QQuickTreeModelAdaptor::moveRows() +{ + QFETCH(int, sourceRow); + QFETCH(bool, expandSource); + QFETCH(int, moveCount); + QFETCH(int, sourceParentRow); + QFETCH(bool, expandSourceParent); + QFETCH(int, destRow); + QFETCH(bool, expandDest); + QFETCH(int, destParentRow); + QFETCH(bool, expandDestParent); + QFETCH(int, expandRow); + QFETCH(int, expandParentRow); + QFETCH(int, signalType); + QFETCH(int, expectedMovedCount); + + TestModel model(ModelRowCount, 1); + model.alternateChildlessRows = false; + QQuickTreeModelAdaptor tma; + tma.setModel(&model); + + const QModelIndex &expandParentIdx = expandParentRow == -1 ? QModelIndex() : model.index(expandParentRow, 0); + if (expandParentIdx.isValid()) { + tma.expand(expandParentIdx); + QVERIFY(tma.isExpanded(expandParentIdx)); + } + const QModelIndex &expandIdx = model.index(expandRow, 0, expandParentIdx); + if (expandIdx.isValid()) { + tma.expand(expandIdx); + QVERIFY(tma.isExpanded(expandIdx)); + } + + const QModelIndex &sourceParentIdx = sourceParentRow == -1 ? QModelIndex() : model.index(sourceParentRow, 0); + if (expandSourceParent && sourceParentIdx.isValid()) { + tma.expand(sourceParentIdx); + QVERIFY(tma.isExpanded(sourceParentIdx)); + } + const QModelIndex &sourceIdx = model.index(sourceRow, 0, sourceParentIdx); + if (expandSource) { + tma.expand(sourceIdx); + QVERIFY(tma.isExpanded(sourceIdx)); + } + + const QModelIndex &destParentIdx = destParentRow == -1 ? QModelIndex() : model.index(destParentRow, 0); + if (expandDestParent && destParentIdx.isValid()) { + tma.expand(destParentIdx); + QVERIFY(tma.isExpanded(destParentIdx)); + } + const QModelIndex &destIdx = model.index(destRow, 0, destParentIdx); + if (expandDest) { + tma.expand(destIdx); + QVERIFY(tma.isExpanded(destIdx)); + } + + int tmaSourceItemIdx = signalType == RowsInserted ? -1 // Not tested if RowsInserted + : tma.itemIndex(sourceIdx); + int tmaDestItemIdx = signalType == RowsRemoved ? -1 : // Not tested if RowsRemoved + destRow == model.rowCount(destParentIdx) ? -1 /* FIXME */ : tma.itemIndex(destIdx); + + QSignalSpy rowsAboutToBeMovedSpy(&tma, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int))); + QSignalSpy rowsMovedSpy(&tma, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int))); + QSignalSpy rowsAboutToBeInsertedSpy(&tma, SIGNAL(rowsAboutToBeInserted(const QModelIndex&, int, int))); + QSignalSpy rowsInsertedSpy(&tma, SIGNAL(rowsInserted(const QModelIndex&, int, int))); + QSignalSpy rowsAboutToBeRemovedSpy(&tma, SIGNAL(rowsAboutToBeRemoved(const QModelIndex&, int, int))); + QSignalSpy rowsRemovedSpy(&tma, SIGNAL(rowsRemoved(const QModelIndex&, int, int))); + + QVERIFY(model.moveRows(sourceParentIdx, sourceRow, moveCount, destParentIdx, destRow)); + + if (signalType != RowsMoved || expectedMovedCount == 0) { + QCOMPARE(rowsAboutToBeMovedSpy.count(), 0); + QCOMPARE(rowsMovedSpy.count(), 0); + } + if (signalType != RowsInserted || expectedMovedCount == 0) { + QCOMPARE(rowsAboutToBeInsertedSpy.count(), 0); + QCOMPARE(rowsInsertedSpy.count(), 0); + } + if (signalType != RowsRemoved || expectedMovedCount == 0) { + QCOMPARE(rowsAboutToBeRemovedSpy.count(), 0); + QCOMPARE(rowsRemovedSpy.count(), 0); + } + + if (expectedMovedCount != 0) { + if (signalType == RowsMoved) { + QCOMPARE(rowsAboutToBeMovedSpy.count(), 1); + QCOMPARE(rowsMovedSpy.count(), 1); + QVariantList rowsAboutToBeMovedArgs = rowsAboutToBeMovedSpy.first(); + QVariantList rowsMovedArgs = rowsMovedSpy.first(); + QCOMPARE(rowsAboutToBeMovedArgs, rowsMovedArgs); + QCOMPARE(rowsAboutToBeMovedArgs.at(0).toModelIndex(), QModelIndex()); + QCOMPARE(rowsAboutToBeMovedArgs.at(1).toInt(), tmaSourceItemIdx); + QCOMPARE(rowsAboutToBeMovedArgs.at(2).toInt(), tmaSourceItemIdx + expectedMovedCount - 1); + QCOMPARE(rowsAboutToBeMovedArgs.at(3).toModelIndex(), QModelIndex()); + if (tmaDestItemIdx != -1) + QCOMPARE(rowsAboutToBeMovedArgs.at(4).toInt(), tmaDestItemIdx); + } else if (signalType == RowsInserted) { + // We only test with one level of expanded children here, so we can do + // exhaustive testing depending on whether the moved row is expanded. + int signalCount = expandSource ? 2 : 1; + QCOMPARE(rowsAboutToBeInsertedSpy.count(), signalCount); + QCOMPARE(rowsInsertedSpy.count(), signalCount); + QVariantList rowsAboutToBeInsertedArgs = rowsAboutToBeInsertedSpy.takeFirst(); + QVariantList rowsInsertedArgs = rowsInsertedSpy.takeFirst(); + QCOMPARE(rowsAboutToBeInsertedArgs, rowsInsertedArgs); + QCOMPARE(rowsAboutToBeInsertedArgs.at(0).toModelIndex(), QModelIndex()); + QCOMPARE(rowsAboutToBeInsertedArgs.at(1).toInt(), tmaDestItemIdx); + if (expandSource) { + QCOMPARE(rowsAboutToBeInsertedArgs.at(2).toInt(), tmaDestItemIdx); + rowsAboutToBeInsertedArgs = rowsAboutToBeInsertedSpy.first(); + rowsInsertedArgs = rowsInsertedSpy.first(); + QCOMPARE(rowsAboutToBeInsertedArgs, rowsInsertedArgs); + QCOMPARE(rowsAboutToBeInsertedArgs.at(0).toModelIndex(), QModelIndex()); + QCOMPARE(rowsAboutToBeInsertedArgs.at(1).toInt(), tmaDestItemIdx + 1); + } + QCOMPARE(rowsAboutToBeInsertedArgs.at(2).toInt(), tmaDestItemIdx + expectedMovedCount - 1); + QCOMPARE(tma.itemIndex(model.index(destRow, 0, destParentIdx)), tmaDestItemIdx); + } else if (signalType == RowsRemoved) { + QCOMPARE(rowsAboutToBeRemovedSpy.count(), 1); + QCOMPARE(rowsRemovedSpy.count(), 1); + QVariantList rowsAboutToBeRemovedArgs = rowsAboutToBeRemovedSpy.first(); + QVariantList rowsRemovedArgs = rowsRemovedSpy.first(); + QCOMPARE(rowsAboutToBeRemovedArgs, rowsRemovedArgs); + QCOMPARE(rowsAboutToBeRemovedArgs.at(0).toModelIndex(), QModelIndex()); + QCOMPARE(rowsAboutToBeRemovedArgs.at(1).toInt(), tmaSourceItemIdx); + QCOMPARE(rowsAboutToBeRemovedArgs.at(2).toInt(), tmaSourceItemIdx + expectedMovedCount - 1); + } + } + QVERIFY(tma.testConsistency()); + compareModels(tma, model); +} + +void tst_QQuickTreeModelAdaptor::selectionForRowRange() +{ + const int ModelRowCount = 9; + const int ModelRowCountLoopStep = 4; + + TestModel model(ModelRowCount, 1); + model.alternateChildlessRows = false; + QQuickTreeModelAdaptor tma; + tma.setModel(&model); + + // NOTE: Some selections may look a bit cryptic. Insert a call to + // tma.dump() before each block if you need to see what's going on. + + for (int i = 0; i < ModelRowCount; i += ModelRowCountLoopStep) { + // Single row selection + const QItemSelection &sel = tma.selectionForRowRange(i, i); + QCOMPARE(sel.count(), 1); + const QItemSelectionRange &range = sel.first(); + QCOMPARE(QModelIndex(range.topLeft()), model.index(i, 0)); + QCOMPARE(QModelIndex(range.bottomRight()), model.index(i, 0)); + } + + for (int i = 0; i < ModelRowCount - ModelRowCountLoopStep; i += ModelRowCountLoopStep) { + // Single range selection + const QItemSelection &sel = tma.selectionForRowRange(i, i + ModelRowCountLoopStep); + QCOMPARE(sel.count(), 1); + const QItemSelectionRange &range = sel.first(); + QCOMPARE(QModelIndex(range.topLeft()), model.index(i, 0)); + QCOMPARE(QModelIndex(range.bottomRight()), model.index(i + ModelRowCountLoopStep, 0)); + } + + { // Select all, no branch expanded + const QItemSelection &sel = tma.selectionForRowRange(0, ModelRowCount - 1); + QCOMPARE(sel.count(), 1); + const QItemSelectionRange &range = sel.first(); + QCOMPARE(QModelIndex(range.topLeft()), model.index(0, 0)); + QCOMPARE(QModelIndex(range.bottomRight()), model.index(ModelRowCount - 1, 0)); + } + + // Expand 1st top-level item + const QModelIndex &parent = model.index(0, 0); + tma.expand(parent); + + { // 1st item expanded, select first 5 rows + const QItemSelection &sel = tma.selectionForRowRange(0, 4); + QCOMPARE(sel.count(), 2); + // We don't know in which order the selection ranges are + // being added, so we iterate and try to find what we expect. + foreach (const QItemSelectionRange &range, sel) { + if (range.topLeft() == model.index(0, 0)) + QCOMPARE(QModelIndex(range.bottomRight()), model.index(0, 0)); + else if (range.topLeft() == model.index(0, 0, parent)) + QCOMPARE(QModelIndex(range.bottomRight()), model.index(3, 0, parent)); + else + QFAIL("Unexpected selection range"); + } + } + + { // 1st item expanded, select first 5 top-level items + const QItemSelection &sel = tma.selectionForRowRange(0, 4 + ModelRowCount); + QCOMPARE(sel.count(), 2); + // We don't know in which order the selection ranges are + // being added, so we iterate and try to find what we expect. + foreach (const QItemSelectionRange &range, sel) { + if (range.topLeft() == model.index(0, 0)) + QCOMPARE(QModelIndex(range.bottomRight()), model.index(4, 0)); + else if (range.topLeft() == model.index(0, 0, parent)) + QCOMPARE(QModelIndex(range.bottomRight()), model.index(ModelRowCount - 1, 0, parent)); + else + QFAIL("Unexpected selection range"); + } + } + + // Expand 2nd top-level item + const QModelIndex &parent2 = model.index(1, 0); + tma.expand(parent2); + + { // 1st two items expanded, select first 5 top-level items + const QItemSelection &sel = tma.selectionForRowRange(0, 4 + 2 * ModelRowCount); + QCOMPARE(sel.count(), 3); + // We don't know in which order the selection ranges are + // being added, so we iterate and try to find what we expect. + foreach (const QItemSelectionRange &range, sel) { + if (range.topLeft() == model.index(0, 0)) + QCOMPARE(QModelIndex(range.bottomRight()), model.index(4, 0)); + else if (range.topLeft() == model.index(0, 0, parent)) + QCOMPARE(QModelIndex(range.bottomRight()), model.index(ModelRowCount - 1, 0, parent)); + else if (range.topLeft() == model.index(0, 0, parent2)) + QCOMPARE(QModelIndex(range.bottomRight()), model.index(ModelRowCount - 1, 0, parent2)); + else + QFAIL("Unexpected selection range"); + } + } + + // Expand 1st child of 1st top-level item + const QModelIndex &parent3 = model.index(0, 0, parent); + tma.expand(parent3); + + { // 1st two items, and 1st child of 1st item expanded, select first 5 rows + const QItemSelection &sel = tma.selectionForRowRange(0, 4); + QCOMPARE(sel.count(), 3); + // We don't know in which order the selection ranges are + // being added, so we iterate and try to find what we expect. + foreach (const QItemSelectionRange &range, sel) { + if (range.topLeft() == model.index(0, 0)) + QCOMPARE(QModelIndex(range.bottomRight()), model.index(0, 0)); + else if (range.topLeft() == model.index(0, 0, parent)) + QCOMPARE(QModelIndex(range.bottomRight()), model.index(0, 0, parent)); + else if (range.topLeft() == model.index(0, 0, parent3)) + QCOMPARE(QModelIndex(range.bottomRight()), model.index(2, 0, parent3)); + else + QFAIL("Unexpected selection range"); + } + } + + { // 1st two items, and 1st child of 1st item expanded, select all + const QItemSelection &sel = tma.selectionForRowRange(0, 4 * ModelRowCount - 1); + QCOMPARE(sel.count(), 4); + // We don't know in which order the selection ranges are + // being added, so we iterate and try to find what we expect. + foreach (const QItemSelectionRange &range, sel) { + if (range.topLeft() == model.index(0, 0)) + QCOMPARE(QModelIndex(range.bottomRight()), model.index(ModelRowCount - 1, 0)); + else if (range.topLeft() == model.index(0, 0, parent)) + QCOMPARE(QModelIndex(range.bottomRight()), model.index(ModelRowCount - 1, 0, parent)); + else if (range.topLeft() == model.index(0, 0, parent2)) + QCOMPARE(QModelIndex(range.bottomRight()), model.index(ModelRowCount - 1, 0, parent2)); + else if (range.topLeft() == model.index(0, 0, parent3)) + QCOMPARE(QModelIndex(range.bottomRight()), model.index(ModelRowCount - 1, 0, parent3)); + else + QFAIL("Unexpected selection range"); + } + } + + { // 1st two items, and 1st child of 1st item expanded, select rows across branches + const QItemSelection &sel = tma.selectionForRowRange(8, 23); + QCOMPARE(sel.count(), 4); + // We don't know in which order the selection ranges are + // being added, so we iterate and try to find what we expect. + foreach (const QItemSelectionRange &range, sel) { + if (range.topLeft() == model.index(1, 0)) + QCOMPARE(QModelIndex(range.bottomRight()), model.index(1, 0)); + else if (range.topLeft() == model.index(1, 0, parent)) + QCOMPARE(QModelIndex(range.bottomRight()), model.index(ModelRowCount - 1, 0, parent)); + else if (range.topLeft() == model.index(0, 0, parent2)) + QCOMPARE(QModelIndex(range.bottomRight()), model.index(3, 0, parent2)); + else if (range.topLeft() == model.index(6, 0, parent3)) + QCOMPARE(QModelIndex(range.bottomRight()), model.index(ModelRowCount - 1, 0, parent3)); + else + QFAIL("Unexpected selection range"); + } + } +} + +QTEST_MAIN(tst_QQuickTreeModelAdaptor) +#include "tst_qquicktreemodeladaptor.moc" |