--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/auto/qitemmodel/tst_qitemmodel.cpp Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,1398 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+#include <QtCore>
+#include <qdebug.h>
+#include "modelstotest.cpp"
+#include <QMetaType>
+
+Q_DECLARE_METATYPE(QModelIndex)
+
+//TESTED_CLASS=
+//TESTED_FILES=gui/itemviews/qstandarditemmodel.h gui/itemviews/qstandarditemmodel.cpp
+
+/*!
+ See modelstotest.cpp for instructions on how to have your model tested with these tests.
+
+ Each test such as rowCount have a _data() function which populate the QTest data with
+ the tests specified by modelstotest.cpp and any extra data needed for that perticular test.
+
+ setupWithNoTestData() fills the QTest data with just the tests and is used by most tests.
+
+ There are some basic qDebug statements sprikled about that might be helpfull for fixing
+ your issues.
+ */
+class tst_QItemModel : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QItemModel() {};
+ virtual ~tst_QItemModel() {};
+
+public slots:
+ void init();
+ void cleanup();
+
+private slots:
+ void nonDestructiveBasicTest_data();
+ void nonDestructiveBasicTest();
+
+ void rowCount_data();
+ void rowCount();
+ void columnCount_data();
+ void columnCount();
+
+ void hasIndex_data();
+ void hasIndex();
+ void index_data();
+ void index();
+
+ void parent_data();
+ void parent();
+
+ void data_data();
+ void data();
+
+ void setData_data();
+ void setData();
+
+ void setHeaderData_data();
+ void setHeaderData();
+
+ void remove_data();
+ void remove();
+
+ void insert_data();
+ void insert();
+
+ void sort_data();
+ void sort();
+
+protected slots:
+ void slot_rowsAboutToRemove(const QModelIndex &parent);
+ void slot_rowsRemoved(const QModelIndex &parent);
+ void slot_columnsAboutToRemove(const QModelIndex &parent);
+ void slot_columnsRemoved(const QModelIndex &parent);
+
+ void slot_rowsAboutToInserted(const QModelIndex &parent);
+ void slot_rowsInserted(const QModelIndex &parent);
+ void slot_columnsAboutToInserted(const QModelIndex &parent);
+ void slot_columnsInserted(const QModelIndex &parent);
+private:
+ void setupWithNoTestData();
+ QAbstractItemModel *currentModel;
+ ModelsToTest *testModels;
+
+ // used by remove()
+ QPersistentModelIndex parentOfRemoved;
+ int afterAboutToRemoveRowCount;
+ int afterRemoveRowCount;
+ int afterAboutToRemoveColumnCount;
+ int afterRemoveColumnCount;
+
+ // remove() recursive
+ bool removeRecursively;
+
+ // used by insert()
+ QPersistentModelIndex parentOfInserted;
+ int afterAboutToInsertRowCount;
+ int afterInsertRowCount;
+ int afterAboutToInsertColumnCount;
+ int afterInsertColumnCount;
+
+ // insert() recursive
+ bool insertRecursively;
+};
+
+void tst_QItemModel::init()
+{
+ testModels = new ModelsToTest();
+ removeRecursively = false;
+ insertRecursively = false;
+}
+
+void tst_QItemModel::cleanup()
+{
+ testModels->cleanupTestArea(currentModel);
+ delete testModels;
+ delete currentModel;
+ currentModel = 0;
+}
+
+void tst_QItemModel::setupWithNoTestData()
+{
+ ModelsToTest modelsToTest;
+ QTest::addColumn<QString>("modelType");
+ QTest::addColumn<bool>("readOnly");
+ QTest::addColumn<bool>("isEmpty");
+ for (int i = 0; i < modelsToTest.tests.size(); ++i) {
+ ModelsToTest::test t = modelsToTest.tests.at(i);
+ bool readOnly = (t.read == ModelsToTest::ReadOnly);
+ bool isEmpty = (t.contains == ModelsToTest::Empty);
+ QTest::newRow(t.modelType.toLatin1().data()) << t.modelType << readOnly << isEmpty;
+ }
+}
+
+void tst_QItemModel::nonDestructiveBasicTest_data()
+{
+ setupWithNoTestData();
+}
+
+/*!
+ nonDestructiveBasicTest trys to call a number of the basic functions (not all)
+ to make sure the model doesn't segfault, testing the functions that makes sense.
+ */
+void tst_QItemModel::nonDestructiveBasicTest()
+{
+ QFETCH(QString, modelType);
+ currentModel = testModels->createModel(modelType);
+
+ QCOMPARE(currentModel->buddy(QModelIndex()), QModelIndex());
+ currentModel->canFetchMore(QModelIndex());
+ QVERIFY(currentModel->columnCount(QModelIndex()) >= 0);
+ QCOMPARE(currentModel->data(QModelIndex()), QVariant());
+ currentModel->fetchMore(QModelIndex());
+ Qt::ItemFlags flags = currentModel->flags(QModelIndex());
+ QVERIFY(flags == Qt::ItemIsDropEnabled || flags == 0);
+ currentModel->hasChildren(QModelIndex());
+ currentModel->hasIndex(0, 0);
+ currentModel->headerData(0, Qt::Horizontal);
+ currentModel->index(0,0), QModelIndex();
+ currentModel->itemData(QModelIndex());
+ QVariant cache;
+ currentModel->match(QModelIndex(), -1, cache);
+ currentModel->mimeTypes();
+ QCOMPARE(currentModel->parent(QModelIndex()), QModelIndex());
+ QVERIFY(currentModel->rowCount() >= 0);
+ QVariant variant;
+ currentModel->setData(QModelIndex(), variant, -1);
+ currentModel->setHeaderData(-1, Qt::Horizontal, QVariant());
+ currentModel->setHeaderData(0, Qt::Horizontal, QVariant());
+ currentModel->setHeaderData(currentModel->columnCount() + 100, Qt::Horizontal, QVariant());
+ QMap<int, QVariant> roles;
+ currentModel->setItemData(QModelIndex(), roles);
+ currentModel->sibling(0,0,QModelIndex());
+ currentModel->span(QModelIndex());
+ currentModel->supportedDropActions();
+ currentModel->revert();
+ currentModel->submit();
+}
+
+
+void tst_QItemModel::rowCount_data()
+{
+ setupWithNoTestData();
+}
+
+/*!
+ Tests model's implimentation of QAbstractItemModel::rowCount() and hasChildren()
+ */
+void tst_QItemModel::rowCount()
+{
+ QFETCH(QString, modelType);
+ currentModel = testModels->createModel(modelType);
+
+ QFETCH(bool, isEmpty);
+ if (isEmpty) {
+ QCOMPARE(currentModel->rowCount(), 0);
+ QCOMPARE(currentModel->hasChildren(), false);
+ }
+ else {
+ QVERIFY(currentModel->rowCount() > 0);
+ QCOMPARE(currentModel->hasChildren(), true);
+ }
+
+ // check top row
+ QModelIndex topIndex = currentModel->index(0, 0, QModelIndex());
+ int rows = currentModel->rowCount(topIndex);
+ QVERIFY(rows >= 0);
+ if (rows > 0)
+ QCOMPARE(currentModel->hasChildren(topIndex), true);
+ else
+ QCOMPARE(currentModel->hasChildren(topIndex), false);
+
+ QModelIndex secondLevelIndex = currentModel->index(0, 0, topIndex);
+ if (secondLevelIndex.isValid()) { // not the top level
+ // check a row count where parent is valid
+ rows = currentModel->rowCount(secondLevelIndex);
+ QVERIFY(rows >= 0);
+ if (rows > 0)
+ QCOMPARE(currentModel->hasChildren(secondLevelIndex), true);
+ else
+ QCOMPARE(currentModel->hasChildren(secondLevelIndex), false);
+ }
+
+ // rowCount is tested more extensivly more later in checkChildren(),
+ // but this catches the big mistakes
+}
+
+void tst_QItemModel::columnCount_data()
+{
+ setupWithNoTestData();
+}
+
+/*!
+ Tests model's implimentation of QAbstractItemModel::columnCount() and hasChildren()
+ */
+void tst_QItemModel::columnCount()
+{
+ QFETCH(QString, modelType);
+ currentModel = testModels->createModel(modelType);
+
+ QFETCH(bool, isEmpty);
+ if (isEmpty) {
+ QCOMPARE(currentModel->hasChildren(), false);
+ }
+ else {
+ QVERIFY(currentModel->columnCount() > 0);
+ QCOMPARE(currentModel->hasChildren(), true);
+ }
+
+ // check top row
+ QModelIndex topIndex = currentModel->index(0, 0, QModelIndex());
+ int columns = currentModel->columnCount(topIndex);
+
+ // check a row count where parent is valid
+ columns = currentModel->columnCount(currentModel->index(0, 0, topIndex));
+ QVERIFY(columns >= 0);
+
+ // columnCount is tested more extensivly more later in checkChildren(),
+ // but this catches the big mistakes
+}
+
+void tst_QItemModel::hasIndex_data()
+{
+ setupWithNoTestData();
+}
+
+/*!
+ Tests model's implimentation of QAbstractItemModel::hasIndex()
+ */
+void tst_QItemModel::hasIndex()
+{
+ QFETCH(QString, modelType);
+ currentModel = testModels->createModel(modelType);
+
+ // Make sure that invalid values returns an invalid index
+ QCOMPARE(currentModel->hasIndex(-2, -2), false);
+ QCOMPARE(currentModel->hasIndex(-2, 0), false);
+ QCOMPARE(currentModel->hasIndex(0, -2), false);
+
+ int rows = currentModel->rowCount();
+ int columns = currentModel->columnCount();
+
+ QCOMPARE(currentModel->hasIndex(rows, columns), false);
+ QCOMPARE(currentModel->hasIndex(rows+1, columns+1), false);
+
+ QFETCH(bool, isEmpty);
+ if (isEmpty)
+ return;
+
+ QCOMPARE(currentModel->hasIndex(0,0), true);
+
+ // hasIndex is tested more extensivly more later in checkChildren(),
+ // but this catches the big mistakes
+}
+
+void tst_QItemModel::index_data()
+{
+ setupWithNoTestData();
+}
+
+/*!
+ Tests model's implimentation of QAbstractItemModel::index()
+ */
+void tst_QItemModel::index()
+{
+ QFETCH(QString, modelType);
+ currentModel = testModels->createModel(modelType);
+
+ // Make sure that invalid values returns an invalid index
+ QCOMPARE(currentModel->index(-2, -2), QModelIndex());
+ QCOMPARE(currentModel->index(-2, 0), QModelIndex());
+ QCOMPARE(currentModel->index(0, -2), QModelIndex());
+
+ QFETCH(bool, isEmpty);
+ if (isEmpty)
+ return;
+
+ int rows = currentModel->rowCount();
+ int columns = currentModel->columnCount();
+
+ // Catch off by one errors
+ QCOMPARE(currentModel->index(rows,columns), QModelIndex());
+ QCOMPARE(currentModel->index(0,0).isValid(), true);
+
+ // Make sure that the same index is always returned
+ QModelIndex a = currentModel->index(0,0);
+ QModelIndex b = currentModel->index(0,0);
+ QVERIFY(a == b);
+
+ // index is tested more extensivly more later in checkChildren(),
+ // but this catches the big mistakes
+}
+
+
+void tst_QItemModel::parent_data()
+{
+ setupWithNoTestData();
+}
+
+/*!
+ A model that returns an index of parent X should also return X when asking
+ for the parent of the index.
+
+ This recursive function does pretty extensive testing on the whole model in an
+ effort to catch edge cases.
+
+ This function assumes that rowCount(), columnCount() and index() work. If they have
+ a bug it will point it out, but the above tests should have already found the basic bugs
+ because it is easier to figure out the problem in those tests then this one.
+ */
+void checkChildren(QAbstractItemModel *currentModel, const QModelIndex &parent, int currentDepth=0)
+{
+ QFETCH(bool, readOnly);
+
+ if (currentModel->canFetchMore(parent))
+ currentModel->fetchMore(parent);
+
+ int rows = currentModel->rowCount(parent);
+ int columns = currentModel->columnCount(parent);
+
+ QCOMPARE(rows > 0, (currentModel->hasChildren(parent)));
+
+ // Some reasuring testing against rows(),columns(), and hasChildren()
+ QVERIFY(rows >= 0);
+ QVERIFY(columns >= 0);
+ if (rows > 0 || columns > 0)
+ QCOMPARE(currentModel->hasChildren(parent), true);
+ else
+ QCOMPARE(currentModel->hasChildren(parent), false);
+
+ //qDebug() << "parent:" << currentModel->data(parent).toString() << "rows:" << rows
+ // << "columns:" << columns << "parent column:" << parent.column();
+
+ QCOMPARE(currentModel->hasIndex(rows+1, 0, parent), false);
+ for (int r = 0; r < rows; ++r) {
+ if (currentModel->canFetchMore(parent))
+ currentModel->fetchMore(parent);
+
+ QCOMPARE(currentModel->hasIndex(r, columns+1, parent), false);
+ for (int c = 0; c < columns; ++c) {
+ QCOMPARE(currentModel->hasIndex(r, c, parent), true);
+ QModelIndex index = currentModel->index(r, c, parent);
+ QCOMPARE(index.isValid(), true);
+
+ if (!readOnly)
+ currentModel->setData(index, "I'm a little tea pot short and stout");
+ QModelIndex modifiedIndex = currentModel->index(r, c, parent);
+ QCOMPARE(index, modifiedIndex);
+
+ // Make sure we get the same index if we request it twice in a row
+ QModelIndex a = currentModel->index(r, c, parent);
+ QModelIndex b = currentModel->index(r, c, parent);
+ QVERIFY(a == b);
+
+ // Some basic checking on the index that is returned
+ QVERIFY(index.model() == currentModel);
+ QCOMPARE(index.row(), r);
+ QCOMPARE(index.column(), c);
+ QCOMPARE(currentModel->data(index, Qt::DisplayRole).isValid(), true);
+
+ // If the next test fails here is some somewhat usefull debug you play with.
+ /*
+ if (currentModel->parent(index) != parent) {
+ qDebug() << r << c << currentDepth << currentModel->data(index).toString()
+ << currentModel->data(parent).toString();
+ qDebug() << index << parent << currentModel->parent(index);
+ QTreeView view;
+ view.setModel(currentModel);
+ view.show();
+ QTest::qWait(9000);
+ }*/
+ QCOMPARE(currentModel->parent(index), parent);
+
+ // recursivly go down
+ if (currentModel->hasChildren(index) && currentDepth < 5) {
+ //qDebug() << r << c << "has children" << currentModel->rowCount(index);
+ checkChildren(currentModel, index, ++currentDepth);
+ // Because this is recursive we will return at the first failure rather then
+ // reporting it over and over
+ if (QTest::currentTestFailed())
+ return;
+ }
+
+ // make sure that after testing the children that the index pointer doesn't change.
+ QModelIndex newerIndex = currentModel->index(r, c, parent);
+ QCOMPARE(index, newerIndex);
+ }
+ }
+}
+
+/*!
+ Tests model's implimentation of QAbstractItemModel::parent()
+ */
+void tst_QItemModel::parent()
+{
+ QFETCH(QString, modelType);
+ currentModel = testModels->createModel(modelType);
+
+ // Make sure the model wont crash and will return an invalid QModelIndex
+ // when asked for the parent of an invalid index.
+ QCOMPARE(currentModel->parent(QModelIndex()), QModelIndex());
+
+ QFETCH(bool, isEmpty);
+ if (isEmpty)
+ return;
+
+ // Common error test #1, make sure that a top level index has a parent
+ // that is a invalid QModelIndex. You model
+ QModelIndex topIndex = currentModel->index(0, 0, QModelIndex());
+ QCOMPARE(currentModel->parent(topIndex), QModelIndex());
+
+ // Common error test #2, make sure that a second level index has a parent
+ // that is the top level index.
+ if (currentModel->rowCount(topIndex) > 0) {
+ QModelIndex childIndex = currentModel->index(0, 0, topIndex);
+ QCOMPARE(currentModel->parent(childIndex), topIndex);
+ }
+
+ // Common error test #3, the second colum has the same children
+ // as the first column in a row.
+ QModelIndex topIndex1 = currentModel->index(0, 1, QModelIndex());
+ if (currentModel->rowCount(topIndex1) > 0) {
+ QModelIndex childIndex = currentModel->index(0, 0, topIndex);
+ QModelIndex childIndex1 = currentModel->index(0, 0, topIndex1);
+ QVERIFY(childIndex != childIndex1);
+ }
+
+ // Full test, walk 10 levels deap through the model making sure that all
+ // parents's children correctly specify their parent
+ QModelIndex top = QModelIndex();
+ checkChildren(currentModel, top);
+}
+
+
+void tst_QItemModel::data_data()
+{
+ setupWithNoTestData();
+}
+
+/*!
+ Tests model's implimentation of QAbstractItemModel::data()
+ */
+void tst_QItemModel::data()
+{
+ QFETCH(QString, modelType);
+ currentModel = testModels->createModel(modelType);
+
+ // Invalid index should return an invalid qvariant
+ QVERIFY(!currentModel->data(QModelIndex()).isValid());
+
+ QFETCH(bool, isEmpty);
+ if (isEmpty)
+ return;
+
+ // A valid index should have a valid qvariant data
+ QVERIFY(currentModel->index(0,0).isValid());
+
+ // shouldn't be able to set data on an invalid index
+ QCOMPARE(currentModel->setData(QModelIndex(), "foo", Qt::DisplayRole), false);
+
+ // General Purpose roles
+ QVariant variant = currentModel->data(currentModel->index(0,0), Qt::ToolTipRole);
+ if (variant.isValid()) {
+ QVERIFY(qVariantCanConvert<QString>(variant));
+ }
+ variant = currentModel->data(currentModel->index(0,0), Qt::StatusTipRole);
+ if (variant.isValid()) {
+ QVERIFY(qVariantCanConvert<QString>(variant));
+ }
+ variant = currentModel->data(currentModel->index(0,0), Qt::WhatsThisRole);
+ if (variant.isValid()) {
+ QVERIFY(qVariantCanConvert<QString>(variant));
+ }
+
+ variant = currentModel->data(currentModel->index(0,0), Qt::SizeHintRole);
+ if (variant.isValid()) {
+ QVERIFY(qVariantCanConvert<QSize>(variant));
+ }
+
+
+ // Appearance roles
+ QVariant fontVariant = currentModel->data(currentModel->index(0,0), Qt::FontRole);
+ if (fontVariant.isValid()) {
+ QVERIFY(qVariantCanConvert<QFont>(fontVariant));
+ }
+
+ QVariant textAlignmentVariant = currentModel->data(currentModel->index(0,0), Qt::TextAlignmentRole);
+ if (textAlignmentVariant.isValid()) {
+ int alignment = textAlignmentVariant.toInt();
+ QVERIFY(alignment == Qt::AlignLeft ||
+ alignment == Qt::AlignRight ||
+ alignment == Qt::AlignHCenter ||
+ alignment == Qt::AlignJustify);
+ }
+
+ QVariant colorVariant = currentModel->data(currentModel->index(0,0), Qt::BackgroundColorRole);
+ if (colorVariant.isValid()) {
+ QVERIFY(qVariantCanConvert<QColor>(colorVariant));
+ }
+
+ colorVariant = currentModel->data(currentModel->index(0,0), Qt::TextColorRole);
+ if (colorVariant.isValid()) {
+ QVERIFY(qVariantCanConvert<QColor>(colorVariant));
+ }
+
+ QVariant checkStateVariant = currentModel->data(currentModel->index(0,0), Qt::CheckStateRole);
+ if (checkStateVariant.isValid()) {
+ int state = checkStateVariant.toInt();
+ QVERIFY(state == Qt::Unchecked ||
+ state == Qt::PartiallyChecked ||
+ state == Qt::Checked);
+ }
+}
+
+void tst_QItemModel::setData_data()
+{
+ setupWithNoTestData();
+}
+
+/*!
+ Tests model's implimentation of QAbstractItemModel::setData()
+ */
+void tst_QItemModel::setData()
+{
+ QFETCH(QString, modelType);
+ currentModel = testModels->createModel(modelType);
+ qRegisterMetaType<QModelIndex>("QModelIndex");
+ QSignalSpy spy(currentModel, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)));
+ QCOMPARE(currentModel->setData(QModelIndex(), QVariant()), false);
+ QCOMPARE(spy.count(), 0);
+
+ QFETCH(bool, isEmpty);
+ if (isEmpty)
+ return;
+
+ QFETCH(bool, readOnly);
+ if (readOnly)
+ return;
+
+ // Populate the test area so we can chage stuff. See: cleanup()
+ QModelIndex topIndex = testModels->populateTestArea(currentModel);
+ QVERIFY(currentModel->hasChildren(topIndex));
+ QModelIndex index = currentModel->index(0, 0, topIndex);
+ QVERIFY(index.isValid());
+
+ spy.clear();
+ QString text = "Index private pointers should always be the same";
+ QCOMPARE(currentModel->setData(index, text, Qt::EditRole), true);
+ QCOMPARE(index.data(Qt::EditRole).toString(), text);
+
+ // Changing the text shouldn't change the layout, parent, pointer etc.
+ QModelIndex changedIndex = currentModel->index(0, 0, topIndex);
+ QCOMPARE(changedIndex, index);
+ QCOMPARE(spy.count(), 1);
+}
+
+void tst_QItemModel::setHeaderData_data()
+{
+ setupWithNoTestData();
+}
+
+/*!
+ Tests model's implimentation of QAbstractItemModel::setHeaderData()
+ */
+void tst_QItemModel::setHeaderData()
+{
+ QFETCH(QString, modelType);
+ currentModel = testModels->createModel(modelType);
+
+ QCOMPARE(currentModel->setHeaderData(-1, Qt::Horizontal, QVariant()), false);
+ QCOMPARE(currentModel->setHeaderData(-1, Qt::Vertical, QVariant()), false);
+
+ QFETCH(bool, isEmpty);
+ if (isEmpty)
+ return;
+
+ QFETCH(bool, readOnly);
+ if (readOnly)
+ return;
+
+ // Populate the test area so we can change stuff. See: cleanup()
+ QModelIndex topIndex = testModels->populateTestArea(currentModel);
+ QVERIFY(currentModel->hasChildren(topIndex));
+ QModelIndex index = currentModel->index(0, 0, topIndex);
+ QVERIFY(index.isValid());
+
+ qRegisterMetaType<Qt::Orientation>("Qt::Orientation");
+ QSignalSpy spy(currentModel, SIGNAL(headerDataChanged( Qt::Orientation, int , int )));
+
+ QString text = "Index private pointers should always be the same";
+ int signalCount = 0;
+ for (int i = 0; i < 4; ++i){
+ if(currentModel->setHeaderData(i, Qt::Horizontal, text)) {
+ QCOMPARE(currentModel->headerData(i, Qt::Horizontal).toString(), text);
+ ++signalCount;
+ }
+ if(currentModel->setHeaderData(i, Qt::Vertical, text)) {
+ QCOMPARE(currentModel->headerData(i, Qt::Vertical).toString(), text);
+ ++signalCount;
+ }
+ }
+ QCOMPARE(spy.count(), signalCount);
+}
+
+void tst_QItemModel::sort_data()
+{
+ setupWithNoTestData();
+}
+
+/*!
+ Tests model's implimentation of QAbstractItemModel::sort()
+ */
+void tst_QItemModel::sort()
+{
+ QFETCH(QString, modelType);
+ currentModel = testModels->createModel(modelType);
+
+ QFETCH(bool, isEmpty);
+ if (isEmpty)
+ return;
+
+ // Populate the test area so we can chage stuff. See: cleanup()
+ QPersistentModelIndex topIndex = testModels->populateTestArea(currentModel);
+ QVERIFY(currentModel->hasChildren(topIndex));
+ QModelIndex index = currentModel->index(0, 0, topIndex);
+ QVERIFY(index.isValid());
+ QSignalSpy spy(currentModel, SIGNAL(layoutChanged()));
+ for (int i=-1; i < 10; ++i){
+ currentModel->sort(i);
+ if (index != currentModel->index(0, 0, topIndex)){
+ QVERIFY(spy.count() > 0);
+ index = currentModel->index(0, 0, topIndex);
+ spy.clear();
+ }
+ }
+ currentModel->sort(99999);
+}
+
+/*!
+ Tests model's implimentation of QAbstractItemModel::removeRow() and QAbstractItemModel::removeColumn()
+ */
+#define START 0
+#define MIDDLE 6
+#define END -1
+#define MANY 9
+#define ALL -1
+#define NOSIGNALS 0
+#define DEFAULTCOUNT 1
+#define DNS 1 // DefaultNumberOfSignals
+#define RECURSIVE true
+#define SUCCESS true
+#define FAIL false
+void tst_QItemModel::remove_data()
+{
+ ModelsToTest modelsToTest;
+ QTest::addColumn<QString>("modelType");
+ QTest::addColumn<bool>("readOnly");
+ QTest::addColumn<bool>("isEmpty");
+
+ QTest::addColumn<int>("start");
+ QTest::addColumn<int>("count");
+
+ QTest::addColumn<int>("numberOfRowsAboutToBeRemovedSignals");
+ QTest::addColumn<int>("numberOfColumnsAboutToBeRemovedSignals");
+ QTest::addColumn<int>("numberOfRowsRemovedSignals");
+ QTest::addColumn<int>("numberOfColumnsRemovedSignals");
+
+ QTest::addColumn<bool>("recursive");
+ QTest::addColumn<int>("recursiveRow");
+ QTest::addColumn<int>("recursiveCount");
+
+ QTest::addColumn<bool>("shouldSucceed");
+
+#define makeTestRow(testName, start, count, sar, srr, sac, src, r, rr, rc, s) \
+ QTest::newRow((t.modelType + testName).toLatin1().data()) << t.modelType << readOnly << isEmpty << \
+ start << count << \
+ sar << srr << sac << src << \
+ r << rr << rc << \
+ s;
+
+ for (int i = 0; i < modelsToTest.tests.size(); ++i) {
+ ModelsToTest::test t = modelsToTest.tests.at(i);
+ QString name = t.modelType;
+ bool readOnly = (t.read == ModelsToTest::ReadOnly);
+ bool isEmpty = (t.contains == ModelsToTest::Empty);
+
+ // half these
+ makeTestRow(":one at the start", START, DEFAULTCOUNT, DNS, DNS, DNS, DNS, !RECURSIVE, 0, 0, SUCCESS);
+ makeTestRow(":one at the middle", MIDDLE, DEFAULTCOUNT, DNS, DNS, DNS, DNS, !RECURSIVE, 0, 0, SUCCESS);
+ makeTestRow(":one at the end", END, DEFAULTCOUNT, DNS, DNS, DNS, DNS, !RECURSIVE, 0, 0, SUCCESS);
+
+ makeTestRow(":many at the start", START, MANY, DNS, DNS, DNS, DNS, !RECURSIVE, 0, 0, SUCCESS);
+ makeTestRow(":many at the middle", MIDDLE, MANY, DNS, DNS, DNS, DNS, !RECURSIVE, 0, 0, SUCCESS);
+ makeTestRow(":many at the end", END, MANY, DNS, DNS, DNS, DNS, !RECURSIVE, 0, 0, SUCCESS);
+
+ makeTestRow(":remove all", START, ALL, DNS, DNS, DNS, DNS, !RECURSIVE, 0, 0, SUCCESS);
+
+ makeTestRow(":none at the start", START, 0, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
+ makeTestRow(":none at the middle", MIDDLE, 0, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
+ makeTestRow(":none at the end", END, 0, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
+
+ makeTestRow(":invalid start, valid count", -99, 0, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
+ makeTestRow(":invalid start, valid count", 9999, 0, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
+ makeTestRow(":invalid start, valid count", -99, 1, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
+ makeTestRow(":invalid start, valid count", 9999, 1, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
+ makeTestRow(":invalid start, valid count", -99, MANY, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
+ makeTestRow(":invalid start, valid count", 9999, MANY, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
+
+ makeTestRow(":valid start, invalid count", START, -2, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
+ makeTestRow(":valid start, invalid count", MIDDLE, -2, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
+ makeTestRow(":valid start, invalid count", END, -2, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
+ makeTestRow(":valid start, invalid count", START, 9999, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
+ makeTestRow(":valid start, invalid count", MIDDLE, 9999, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
+ makeTestRow(":valid start, invalid count", END, 9999, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
+
+ // Recursive remove's might assert, havn't decided yet...
+ //makeTestRow(":one at the start recursivly", START, DEFAULTCOUNT, 2, DNS, 2, DNS, RECURSIVE, START, DEFAULTCOUNT, FAIL);
+ //makeTestRow(":one at the middle recursivly", MIDDLE, DEFAULTCOUNT, 2, DNS, 2, DNS, RECURSIVE, START, DEFAULTCOUNT, SUCCESS);
+ //makeTestRow(":one at the end recursivly", END, DEFAULTCOUNT, 2, DNS, 2, DNS, RECURSIVE, START, DEFAULTCOUNT, SUCCESS);
+ }
+}
+
+void tst_QItemModel::remove()
+{
+ QFETCH(QString, modelType);
+
+ currentModel = testModels->createModel(modelType);
+
+ QFETCH(bool, readOnly);
+ if (readOnly)
+ return;
+
+ QFETCH(int, start);
+ QFETCH(int, count);
+
+ QFETCH(bool, recursive);
+ removeRecursively = recursive;
+
+/*!
+ Removes count number of rows starting at start
+ if count is -1 it removes all rows
+ if start is -1 then it starts at the last row - count
+ */
+ QFETCH(bool, shouldSucceed);
+
+ // Populate the test area so we can remove something. See: cleanup()
+ // parentOfRemoved is stored so that the slots can make sure parentOfRemoved is the index that is emited.
+ parentOfRemoved = testModels->populateTestArea(currentModel);
+
+ if (count == -1)
+ count = currentModel->rowCount(parentOfRemoved);
+ if (start == -1)
+ start = currentModel->rowCount(parentOfRemoved)-count;
+
+ if (currentModel->rowCount(parentOfRemoved) == 0 ||
+ currentModel->columnCount(parentOfRemoved) == 0) {
+ qWarning() << "model test area doesn't have any rows or columns, can't fully test remove(). Skipping";
+ return;
+ }
+
+ //qDebug() << "remove start:" << start << "count:" << count << "rowCount:" << currentModel->rowCount(parentOfRemoved);
+
+ // When a row or column is removed there should be two signals.
+ // Watch to make sure they are emited and get the row/column count when they do get emited by connecting them to a slot
+ qRegisterMetaType<QModelIndex>("QModelIndex");
+ QSignalSpy columnsAboutToBeRemovedSpy(currentModel, SIGNAL(columnsAboutToBeRemoved( const QModelIndex &, int , int )));
+ QSignalSpy rowsAboutToBeRemovedSpy(currentModel, SIGNAL(rowsAboutToBeRemoved( const QModelIndex &, int , int )));
+ QSignalSpy columnsRemovedSpy(currentModel, SIGNAL(columnsRemoved( const QModelIndex &, int, int )));
+ QSignalSpy rowsRemovedSpy(currentModel, SIGNAL(rowsRemoved( const QModelIndex &, int, int )));
+ QSignalSpy modelResetSpy(currentModel, SIGNAL(modelReset()));
+ QSignalSpy modelLayoutChangedSpy(currentModel, SIGNAL(layoutChanged()));
+
+ QFETCH(int, numberOfRowsAboutToBeRemovedSignals);
+ QFETCH(int, numberOfColumnsAboutToBeRemovedSignals);
+ QFETCH(int, numberOfRowsRemovedSignals);
+ QFETCH(int, numberOfColumnsRemovedSignals);
+
+ //
+ // test removeRow()
+ //
+ connect(currentModel, SIGNAL(rowsAboutToBeRemoved( const QModelIndex &, int , int )),
+ this, SLOT(slot_rowsAboutToRemove(const QModelIndex &)));
+ connect(currentModel, SIGNAL(rowsRemoved( const QModelIndex &, int , int )),
+ this, SLOT(slot_rowsRemoved(const QModelIndex &)));
+ int beforeRemoveRowCount = currentModel->rowCount(parentOfRemoved);
+ QPersistentModelIndex dyingIndex = currentModel->index(start + count + 1, 0, parentOfRemoved);
+ QCOMPARE(currentModel->removeRows(start, count, parentOfRemoved), shouldSucceed);
+ currentModel->submit();
+ if (shouldSucceed && dyingIndex.isValid())
+ QCOMPARE(dyingIndex.row(), start + 1);
+
+ if (rowsAboutToBeRemovedSpy.count() > 0){
+ QList<QVariant> arguments = rowsAboutToBeRemovedSpy.at(0);
+ QModelIndex parent = (qvariant_cast<QModelIndex>(arguments.at(0)));
+ int first = arguments.at(1).toInt();
+ int last = arguments.at(2).toInt();
+ QCOMPARE(first, start);
+ QCOMPARE(last, start + count - 1);
+ QVERIFY(parentOfRemoved == parent);
+ }
+
+ if (rowsRemovedSpy.count() > 0){
+ QList<QVariant> arguments = rowsRemovedSpy.at(0);
+ QModelIndex parent = (qvariant_cast<QModelIndex>(arguments.at(0)));
+ int first = arguments.at(1).toInt();
+ int last = arguments.at(2).toInt();
+ QCOMPARE(first, start);
+ QCOMPARE(last, start + count - 1);
+ QVERIFY(parentOfRemoved == parent);
+ }
+
+ // Only the row signals should have been emited
+ if (modelResetSpy.count() >= 1 || modelLayoutChangedSpy.count() >=1 ){
+ QCOMPARE(columnsAboutToBeRemovedSpy.count(), 0);
+ QCOMPARE(rowsAboutToBeRemovedSpy.count(), 0);
+ QCOMPARE(columnsRemovedSpy.count(), 0);
+ QCOMPARE(rowsRemovedSpy.count(), 0);
+ }
+ else {
+ QCOMPARE(columnsAboutToBeRemovedSpy.count(), 0);
+ QCOMPARE(rowsAboutToBeRemovedSpy.count(), numberOfRowsAboutToBeRemovedSignals);
+ QCOMPARE(columnsRemovedSpy.count(), 0);
+ QCOMPARE(rowsRemovedSpy.count(), numberOfRowsRemovedSignals);
+ }
+
+ // The row count should only change *after* rowsAboutToBeRemoved has been emited
+ //qDebug() << beforeRemoveRowCount << afterAboutToRemoveRowCount << afterRemoveRowCount << currentModel->rowCount(parentOfRemoved);
+ if (shouldSucceed) {
+ if (modelResetSpy.count() == 0 && modelLayoutChangedSpy.count() == 0){
+ QCOMPARE(afterAboutToRemoveRowCount, beforeRemoveRowCount);
+ QCOMPARE(afterRemoveRowCount, beforeRemoveRowCount-count-(numberOfRowsRemovedSignals-1));
+ }
+ if (modelResetSpy.count() == 0 )
+ QCOMPARE(currentModel->rowCount(parentOfRemoved), beforeRemoveRowCount-count-(numberOfRowsRemovedSignals-1));
+ }
+ else {
+ if (recursive)
+ QCOMPARE(currentModel->rowCount(parentOfRemoved), beforeRemoveRowCount-1);
+ else
+ QCOMPARE(currentModel->rowCount(parentOfRemoved), beforeRemoveRowCount);
+
+ }
+ disconnect(currentModel, SIGNAL(rowsAboutToBeRemoved( const QModelIndex &, int , int )),
+ this, SLOT(slot_rowsAboutToRemove(const QModelIndex &)));
+ disconnect(currentModel, SIGNAL(rowsRemoved( const QModelIndex &, int , int )),
+ this, SLOT(slot_rowsRemoved(const QModelIndex &)));
+ modelResetSpy.clear();
+ QCOMPARE(modelResetSpy.count(), 0);
+
+ //
+ // Test remove column
+ //
+ connect(currentModel, SIGNAL(columnsAboutToBeRemoved( const QModelIndex &, int , int )),
+ this, SLOT(slot_columnsAboutToRemove(const QModelIndex &)));
+ connect(currentModel, SIGNAL(columnsRemoved( const QModelIndex &, int , int )),
+ this, SLOT(slot_columnsRemoved(const QModelIndex &)));
+ int beforeRemoveColumnCount = currentModel->columnCount(parentOfRemoved);
+
+ // Some models don't let you remove the column, only row
+ if (currentModel->removeColumns(start, count, parentOfRemoved)) {
+ currentModel->submit();
+ // Didn't reset the rows, so they should still be at the same value
+ if (modelResetSpy.count() >= 1 || modelLayoutChangedSpy.count() >= 1){
+ QCOMPARE(columnsAboutToBeRemovedSpy.count(), 0);
+ //QCOMPARE(rowsAboutToBeRemovedSpy.count(), numberOfRowsAboutToBeRemovedSignals);
+ QCOMPARE(columnsRemovedSpy.count(), 0);
+ //QCOMPARE(rowsRemovedSpy.count(), numberOfRowsRemovedSignals);
+ }
+ else {
+ QCOMPARE(columnsAboutToBeRemovedSpy.count(), numberOfColumnsAboutToBeRemovedSignals);
+ QCOMPARE(rowsAboutToBeRemovedSpy.count(), numberOfRowsAboutToBeRemovedSignals);
+ QCOMPARE(columnsRemovedSpy.count(), numberOfColumnsRemovedSignals);
+ QCOMPARE(rowsRemovedSpy.count(), numberOfRowsRemovedSignals);
+ }
+
+ // The column count should only change *after* rowsAboutToBeRemoved has been emited
+ if (shouldSucceed) {
+ if (modelResetSpy.count() == 0 && modelLayoutChangedSpy.count() == 0){
+ QCOMPARE(afterAboutToRemoveColumnCount, beforeRemoveColumnCount);
+ QCOMPARE(afterRemoveColumnCount, beforeRemoveColumnCount-count-(numberOfColumnsRemovedSignals-1));
+ }
+ if (modelResetSpy.count() == 0)
+ QCOMPARE(currentModel->columnCount(parentOfRemoved), beforeRemoveColumnCount-count-(numberOfColumnsRemovedSignals-1));
+ }
+ else
+ QCOMPARE(currentModel->rowCount(parentOfRemoved), beforeRemoveRowCount);
+ }
+ disconnect(currentModel, SIGNAL(columnsAboutToBeRemoved( const QModelIndex &, int , int )),
+ this, SLOT(slot_columnsAboutToRemove(const QModelIndex &)));
+ disconnect(currentModel, SIGNAL(columnsRemoved( const QModelIndex &, int , int )),
+ this, SLOT(slot_columnsRemoved(const QModelIndex &)));
+
+ if (columnsAboutToBeRemovedSpy.count() > 0){
+ QList<QVariant> arguments = columnsAboutToBeRemovedSpy.at(0);
+ QModelIndex parent = (qvariant_cast<QModelIndex>(arguments.at(0)));
+ int first = arguments.at(1).toInt();
+ int last = arguments.at(2).toInt();
+ QCOMPARE(first, start);
+ QCOMPARE(last, start + count - 1);
+ QVERIFY(parentOfRemoved == parent);
+ }
+
+ if (columnsRemovedSpy.count() > 0){
+ QList<QVariant> arguments = columnsRemovedSpy.at(0);
+ QModelIndex parent = (qvariant_cast<QModelIndex>(arguments.at(0)));
+ int first = arguments.at(1).toInt();
+ int last = arguments.at(2).toInt();
+ QCOMPARE(first, start);
+ QCOMPARE(last, start + count - 1);
+ QVERIFY(parentOfRemoved == parent);
+ }
+
+ // Cleanup the test area because remove::() is called multiple times in a test
+ testModels->cleanupTestArea(currentModel);
+}
+
+/*!
+ Developers like to use the slots to then *do* something on the model so it needs to be
+ in a working state.
+ */
+void verifyState(QAbstractItemModel *currentModel) {
+ // Make sure the model isn't confused right now and still knows what is root
+ if (currentModel->hasChildren()) {
+ QCOMPARE(currentModel->hasIndex(0, 0), true);
+ QModelIndex index = currentModel->index(0,0);
+ QCOMPARE(index.isValid(), true);
+ QCOMPARE(currentModel->parent(index).isValid(), false);
+ } else {
+ QModelIndex index = currentModel->index(0,0);
+ QCOMPARE(index.isValid(), false);
+ }
+}
+
+void tst_QItemModel::slot_rowsAboutToRemove(const QModelIndex &parent)
+{
+ QVERIFY(parentOfRemoved == parent);
+ //qDebug() << "slot_rowsAboutToRemove" << currentModel->rowCount(parent);
+ afterAboutToRemoveRowCount = currentModel->rowCount(parent);
+ // hasChildren() should still work
+ if (afterAboutToRemoveRowCount > 0)
+ QCOMPARE(currentModel->hasChildren(parent), true);
+ else
+ QCOMPARE(currentModel->hasChildren(parent), false);
+
+ verifyState(currentModel);
+
+ // This does happen
+ if (removeRecursively) {
+ QFETCH(int, recursiveRow);
+ QFETCH(int, recursiveCount);
+ //qDebug() << recursiveRow << recursiveCount;
+ removeRecursively = false;
+ QCOMPARE(currentModel->removeRows(recursiveRow, recursiveCount, parent), true);
+ }
+}
+
+void tst_QItemModel::slot_rowsRemoved(const QModelIndex &parent)
+{
+ QVERIFY(parentOfRemoved == parent);
+ //qDebug() << "slot_rowsRemoved" << currentModel->rowCount(parent);
+ afterRemoveRowCount = currentModel->rowCount(parent);
+ if (afterRemoveRowCount > 0)
+ QCOMPARE(currentModel->hasChildren(parent), true);
+ else
+ QCOMPARE(currentModel->hasChildren(parent), false);
+
+ verifyState(currentModel);
+}
+
+void tst_QItemModel::slot_columnsAboutToRemove(const QModelIndex &parent)
+{
+ QVERIFY(parentOfRemoved == parent);
+ afterAboutToRemoveColumnCount = currentModel->columnCount(parent);
+ // hasChildren() should still work
+ if (afterAboutToRemoveColumnCount > 0 && currentModel->rowCount(parent) > 0)
+ QCOMPARE(currentModel->hasChildren(parent), true);
+ else
+ QCOMPARE(currentModel->hasChildren(parent), false);
+
+ verifyState(currentModel);
+}
+
+void tst_QItemModel::slot_columnsRemoved(const QModelIndex &parent)
+{
+ QVERIFY(parentOfRemoved == parent);
+ afterRemoveColumnCount = currentModel->columnCount(parent);
+ if (afterRemoveColumnCount > 0)
+ QCOMPARE(currentModel->hasChildren(parent), true);
+ else
+ QCOMPARE(currentModel->hasChildren(parent), false);
+
+ verifyState(currentModel);
+}
+
+/*!
+ Tests the model's insertRow/Column()
+ */
+void tst_QItemModel::insert_data()
+{
+ ModelsToTest modelsToTest;
+ QTest::addColumn<QString>("modelType");
+ QTest::addColumn<bool>("readOnly");
+ QTest::addColumn<bool>("isEmpty");
+
+ QTest::addColumn<int>("start");
+ QTest::addColumn<int>("count");
+
+ QTest::addColumn<int>("numberOfRowsAboutToBeInsertedSignals");
+ QTest::addColumn<int>("numberOfColumnsAboutToBeInsertedSignals");
+ QTest::addColumn<int>("numberOfRowsInsertedSignals");
+ QTest::addColumn<int>("numberOfColumnsInsertedSignals");
+
+ QTest::addColumn<bool>("recursive");
+ QTest::addColumn<int>("recursiveRow");
+ QTest::addColumn<int>("recursiveCount");
+
+ QTest::addColumn<bool>("shouldSucceed");
+
+#define makeTestRow(testName, start, count, sar, srr, sac, src, r, rr, rc, s) \
+ QTest::newRow((t.modelType + testName).toLatin1().data()) << t.modelType << readOnly << isEmpty << \
+ start << count << \
+ sar << srr << sac << src << \
+ r << rr << rc << \
+ s;
+
+ for (int i = 0; i < modelsToTest.tests.size(); ++i) {
+ ModelsToTest::test t = modelsToTest.tests.at(i);
+ QString name = t.modelType;
+ bool readOnly = (t.read == ModelsToTest::ReadOnly);
+ bool isEmpty = (t.contains == ModelsToTest::Empty);
+
+ // half these
+ makeTestRow(":one at the start", START, DEFAULTCOUNT, DNS, DNS, DNS, DNS, !RECURSIVE, 0, 0, SUCCESS);
+ makeTestRow(":one at the middle", MIDDLE, DEFAULTCOUNT, DNS, DNS, DNS, DNS, !RECURSIVE, 0, 0, SUCCESS);
+ makeTestRow(":one at the end", END, DEFAULTCOUNT, DNS, DNS, DNS, DNS, !RECURSIVE, 0, 0, SUCCESS);
+
+ makeTestRow(":many at the start", START, MANY, DNS, DNS, DNS, DNS, !RECURSIVE, 0, 0, SUCCESS);
+ makeTestRow(":many at the middle", MIDDLE, MANY, DNS, DNS, DNS, DNS, !RECURSIVE, 0, 0, SUCCESS);
+ makeTestRow(":many at the end", END, MANY, DNS, DNS, DNS, DNS, !RECURSIVE, 0, 0, SUCCESS);
+
+ makeTestRow(":add row count", START, ALL, DNS, DNS, DNS, DNS, !RECURSIVE, 0, 0, SUCCESS);
+
+ makeTestRow(":none at the start", START, 0, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
+ makeTestRow(":none at the middle", MIDDLE, 0, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
+ makeTestRow(":none at the end", END, 0, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
+
+ makeTestRow(":invalid start, valid count", -99, 0, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
+ makeTestRow(":invalid start, valid count", 9999, 0, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
+ makeTestRow(":invalid start, valid count", -99, 1, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
+ makeTestRow(":invalid start, valid count", 9999, 1, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
+ makeTestRow(":invalid start, valid count", -99, MANY, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
+ makeTestRow(":invalid start, valid count", 9999, MANY, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
+
+ makeTestRow(":valid start, invalid count", START, -2, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
+ makeTestRow(":valid start, invalid count", MIDDLE, -2, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
+ makeTestRow(":valid start, invalid count", END, -2, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
+
+ // Recursive insert's might assert, havn't decided yet...
+ //makeTestRow(":one at the start recursivly", START, DEFAULTCOUNT, 2, DNS, 2, DNS, RECURSIVE, START, DEFAULTCOUNT, FAIL);
+ //makeTestRow(":one at the middle recursivly", MIDDLE, DEFAULTCOUNT, 2, DNS, 2, DNS, RECURSIVE, START, DEFAULTCOUNT, SUCCESS);
+ //makeTestRow(":one at the end recursivly", END, DEFAULTCOUNT, 2, DNS, 2, DNS, RECURSIVE, START, DEFAULTCOUNT, SUCCESS);
+ }
+}
+
+void tst_QItemModel::insert()
+{
+ QFETCH(QString, modelType);
+ currentModel = testModels->createModel(modelType);
+
+ QFETCH(bool, readOnly);
+ if (readOnly)
+ return;
+
+ QFETCH(int, start);
+ QFETCH(int, count);
+
+ QFETCH(bool, recursive);
+ insertRecursively = recursive;
+
+/*!
+ Inserts count number of rows starting at start
+ if count is -1 it inserts all rows
+ if start is -1 then it starts at the last row - count
+ */
+ QFETCH(bool, shouldSucceed);
+
+ // Populate the test area so we can insert something. See: cleanup()
+ // parentOfInserted is stored so that the slots can make sure parentOfInserted is the index that is emited.
+ parentOfInserted = testModels->populateTestArea(currentModel);
+
+ if (count == -1)
+ count = currentModel->rowCount(parentOfInserted);
+ if (start == -1)
+ start = currentModel->rowCount(parentOfInserted)-count;
+
+ if (currentModel->rowCount(parentOfInserted) == 0 ||
+ currentModel->columnCount(parentOfInserted) == 0) {
+ qWarning() << "model test area doesn't have any rows, can't fully test insert(). Skipping";
+ return;
+ }
+
+ //qDebug() << "insert start:" << start << "count:" << count << "rowCount:" << currentModel->rowCount(parentOfInserted);
+
+ // When a row or column is inserted there should be two signals.
+ // Watch to make sure they are emited and get the row/column count when they do get emited by connecting them to a slot
+ qRegisterMetaType<QModelIndex>("QModelIndex");
+ QSignalSpy columnsAboutToBeInsertedSpy(currentModel, SIGNAL(columnsAboutToBeInserted( const QModelIndex &, int , int )));
+ QSignalSpy rowsAboutToBeInsertedSpy(currentModel, SIGNAL(rowsAboutToBeInserted( const QModelIndex &, int , int )));
+ QSignalSpy columnsInsertedSpy(currentModel, SIGNAL(columnsInserted( const QModelIndex &, int, int )));
+ QSignalSpy rowsInsertedSpy(currentModel, SIGNAL(rowsInserted( const QModelIndex &, int, int )));
+ QSignalSpy modelResetSpy(currentModel, SIGNAL(modelReset()));
+ QSignalSpy modelLayoutChangedSpy(currentModel, SIGNAL(layoutChanged()));
+
+ QFETCH(int, numberOfRowsAboutToBeInsertedSignals);
+ QFETCH(int, numberOfColumnsAboutToBeInsertedSignals);
+ QFETCH(int, numberOfRowsInsertedSignals);
+ QFETCH(int, numberOfColumnsInsertedSignals);
+
+ //
+ // test insertRow()
+ //
+ connect(currentModel, SIGNAL(rowsAboutToBeInserted( const QModelIndex &, int , int )),
+ this, SLOT(slot_rowsAboutToInserted(const QModelIndex &)));
+ connect(currentModel, SIGNAL(rowsInserted( const QModelIndex &, int , int )),
+ this, SLOT(slot_rowsInserted(const QModelIndex &)));
+ int beforeInsertRowCount = currentModel->rowCount(parentOfInserted);
+ QCOMPARE(currentModel->insertRows(start, count, parentOfInserted), shouldSucceed);
+ currentModel->submit();
+
+ if (rowsAboutToBeInsertedSpy.count() > 0){
+ QList<QVariant> arguments = rowsAboutToBeInsertedSpy.at(0);
+ QModelIndex parent = (qvariant_cast<QModelIndex>(arguments.at(0)));
+ int first = arguments.at(1).toInt();
+ int last = arguments.at(2).toInt();
+ QCOMPARE(first, start);
+ QCOMPARE(last, start + count - 1);
+ QVERIFY(parentOfInserted == parent);
+ }
+
+ if (rowsInsertedSpy.count() > 0){
+ QList<QVariant> arguments = rowsInsertedSpy.at(0);
+ QModelIndex parent = (qvariant_cast<QModelIndex>(arguments.at(0)));
+ int first = arguments.at(1).toInt();
+ int last = arguments.at(2).toInt();
+ QCOMPARE(first, start);
+ QCOMPARE(last, start + count - 1);
+ QVERIFY(parentOfInserted == parent);
+ }
+
+ // Only the row signals should have been emited
+ if (modelResetSpy.count() >= 1 || modelLayoutChangedSpy.count() >= 1) {
+ QCOMPARE(columnsAboutToBeInsertedSpy.count(), 0);
+ QCOMPARE(rowsAboutToBeInsertedSpy.count(), 0);
+ QCOMPARE(columnsInsertedSpy.count(), 0);
+ QCOMPARE(rowsInsertedSpy.count(), 0);
+ }
+ else {
+ QCOMPARE(columnsAboutToBeInsertedSpy.count(), 0);
+ QCOMPARE(rowsAboutToBeInsertedSpy.count(), numberOfRowsAboutToBeInsertedSignals);
+ QCOMPARE(columnsInsertedSpy.count(), 0);
+ QCOMPARE(rowsInsertedSpy.count(), numberOfRowsInsertedSignals);
+ }
+ // The row count should only change *after* rowsAboutToBeInserted has been emited
+ //qDebug() << beforeInsertRowCount << afterAboutToInsertRowCount << afterInsertRowCount << currentModel->rowCount(parentOfInserted);
+ if (shouldSucceed) {
+ if (modelResetSpy.count() == 0 && modelLayoutChangedSpy.count() == 0) {
+ QCOMPARE(afterAboutToInsertRowCount, beforeInsertRowCount);
+ QCOMPARE(afterInsertRowCount, beforeInsertRowCount+count+(numberOfRowsInsertedSignals-1));
+ }
+ if (modelResetSpy.count() == 0)
+ QCOMPARE(currentModel->rowCount(parentOfInserted), beforeInsertRowCount+count+(numberOfRowsInsertedSignals-1));
+ }
+ else {
+ if (recursive)
+ QCOMPARE(currentModel->rowCount(parentOfInserted), beforeInsertRowCount+1);
+ else
+ QCOMPARE(currentModel->rowCount(parentOfInserted), beforeInsertRowCount);
+
+ }
+ disconnect(currentModel, SIGNAL(rowsAboutToBeInserted( const QModelIndex &, int , int )),
+ this, SLOT(slot_rowsAboutToInserted(const QModelIndex &)));
+ disconnect(currentModel, SIGNAL(rowsInserted( const QModelIndex &, int , int )),
+ this, SLOT(slot_rowsInserted(const QModelIndex &)));
+ modelResetSpy.clear();
+
+ //
+ // Test insertColumn()
+ //
+ connect(currentModel, SIGNAL(columnsAboutToBeInserted( const QModelIndex &, int , int )),
+ this, SLOT(slot_columnsAboutToInserted(const QModelIndex &)));
+ connect(currentModel, SIGNAL(columnsInserted( const QModelIndex &, int , int )),
+ this, SLOT(slot_columnsInserted(const QModelIndex &)));
+ int beforeInsertColumnCount = currentModel->columnCount(parentOfInserted);
+
+ // Some models don't let you insert the column, only row
+ if (currentModel->insertColumns(start, count, parentOfInserted)) {
+ currentModel->submit();
+ if (modelResetSpy.count() >= 1 || modelLayoutChangedSpy.count() >= 1) {
+ // Didn't reset the rows, so they should still be at the same value
+ QCOMPARE(columnsAboutToBeInsertedSpy.count(), 0);
+ //QCOMPARE(rowsAboutToBeInsertedSpy.count(), numberOfRowsAboutToBeInsertedSignals);
+ QCOMPARE(columnsInsertedSpy.count(), 0);
+ //QCOMPARE(rowsInsertedSpy.count(), numberOfRowsInsertedSignals);
+ }
+ else {
+ // Didn't reset the rows, so they should still be at the same value
+ QCOMPARE(columnsAboutToBeInsertedSpy.count(), numberOfColumnsAboutToBeInsertedSignals);
+ QCOMPARE(rowsAboutToBeInsertedSpy.count(), numberOfRowsAboutToBeInsertedSignals);
+ QCOMPARE(columnsInsertedSpy.count(), numberOfColumnsInsertedSignals);
+ QCOMPARE(rowsInsertedSpy.count(), numberOfRowsInsertedSignals);
+ }
+ // The column count should only change *after* rowsAboutToBeInserted has been emited
+ if (shouldSucceed) {
+ if (modelResetSpy.count() == 0 && modelLayoutChangedSpy.count() == 0) {
+ QCOMPARE(afterAboutToInsertColumnCount, beforeInsertColumnCount);
+ QCOMPARE(afterInsertColumnCount, beforeInsertColumnCount+count+(numberOfColumnsInsertedSignals-1));
+ }
+ if (modelResetSpy.count() == 0)
+ QCOMPARE(currentModel->columnCount(parentOfInserted), beforeInsertColumnCount+count+(numberOfColumnsInsertedSignals-1));
+ }
+ else
+ QCOMPARE(currentModel->rowCount(parentOfInserted), beforeInsertRowCount);
+ }
+ disconnect(currentModel, SIGNAL(columnsAboutToBeInserted( const QModelIndex &, int , int )),
+ this, SLOT(slot_columnsAboutToInserted(const QModelIndex &)));
+ disconnect(currentModel, SIGNAL(columnsInserted( const QModelIndex &, int , int )),
+ this, SLOT(slot_columnsInserted(const QModelIndex &)));
+
+ if (columnsAboutToBeInsertedSpy.count() > 0){
+ QList<QVariant> arguments = columnsAboutToBeInsertedSpy.at(0);
+ QModelIndex parent = (qvariant_cast<QModelIndex>(arguments.at(0)));
+ int first = arguments.at(1).toInt();
+ int last = arguments.at(2).toInt();
+ QCOMPARE(first, start);
+ QCOMPARE(last, start + count - 1);
+ QVERIFY(parentOfInserted == parent);
+ }
+
+ if (columnsInsertedSpy.count() > 0){
+ QList<QVariant> arguments = columnsInsertedSpy.at(0);
+ QModelIndex parent = (qvariant_cast<QModelIndex>(arguments.at(0)));
+ int first = arguments.at(1).toInt();
+ int last = arguments.at(2).toInt();
+ QCOMPARE(first, start);
+ QCOMPARE(last, start + count - 1);
+ QVERIFY(parentOfInserted == parent);
+ }
+
+ // Cleanup the test area because insert::() is called multiple times in a test
+ testModels->cleanupTestArea(currentModel);
+}
+
+void tst_QItemModel::slot_rowsAboutToInserted(const QModelIndex &parent)
+{
+ QVERIFY(parentOfInserted == parent);
+ //qDebug() << "slot_rowsAboutToInsert" << currentModel->rowCount(parent);
+ afterAboutToInsertRowCount = currentModel->rowCount(parent);
+ bool hasChildren = currentModel->hasChildren(parent);
+ bool hasDimensions = currentModel->columnCount(parent) > 0 && currentModel->rowCount(parent) > 0;
+ QCOMPARE(hasChildren, hasDimensions);
+ verifyState(currentModel);
+
+ // This does happen
+ if (insertRecursively) {
+ QFETCH(int, recursiveRow);
+ QFETCH(int, recursiveCount);
+ //qDebug() << recursiveRow << recursiveCount;
+ insertRecursively = false;
+ QCOMPARE(currentModel->insertRows(recursiveRow, recursiveCount, parent), true);
+ }
+}
+
+void tst_QItemModel::slot_rowsInserted(const QModelIndex &parent)
+{
+ QVERIFY(parentOfInserted == parent);
+ afterInsertRowCount = currentModel->rowCount(parent);
+ bool hasChildren = currentModel->hasChildren(parent);
+ bool hasDimensions = currentModel->columnCount(parent) > 0 && currentModel->rowCount(parent) > 0;
+ QCOMPARE(hasChildren, hasDimensions);
+ verifyState(currentModel);
+}
+
+void tst_QItemModel::slot_columnsAboutToInserted(const QModelIndex &parent)
+{
+ QVERIFY(parentOfInserted == parent);
+ afterAboutToInsertColumnCount = currentModel->columnCount(parent);
+ bool hasChildren = currentModel->hasChildren(parent);
+ bool hasDimensions = currentModel->columnCount(parent) > 0 && currentModel->rowCount(parent) > 0;
+ QCOMPARE(hasChildren, hasDimensions);
+ verifyState(currentModel);
+}
+
+void tst_QItemModel::slot_columnsInserted(const QModelIndex &parent)
+{
+ QVERIFY(parentOfInserted == parent);
+ afterInsertColumnCount = currentModel->columnCount(parent);
+ bool hasChildren = currentModel->hasChildren(parent);
+ bool hasDimensions = currentModel->columnCount(parent) > 0 && currentModel->rowCount(parent) > 0;
+ QCOMPARE(hasChildren, hasDimensions);
+ verifyState(currentModel);
+}
+
+QTEST_MAIN(tst_QItemModel)
+#include "tst_qitemmodel.moc"