197 changes: 197 additions & 0 deletions tests/qt_modeltest/dynamictreemodel.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
/****************************************************************************
**
** Copyright (C) 2009 Stephen Kelly <steveire@gmail.com>
** 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$
** GNU Lesser General Public License Usage
** 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.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 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 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#ifndef DYNAMICTREEMODEL_H
#define DYNAMICTREEMODEL_H

#include <QtCore/QAbstractItemModel>

#include <QtCore/QHash>
#include <QtCore/QList>


class DynamicTreeModel : public QAbstractItemModel
{
Q_OBJECT

public:
DynamicTreeModel(QObject *parent = 0);

QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
QModelIndex parent(const QModelIndex &index) const;
int rowCount(const QModelIndex &index = QModelIndex()) const;
int columnCount(const QModelIndex &index = QModelIndex()) const;

QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;

void clear();

protected slots:

/**
Finds the parent id of the string with id @p searchId.
Returns -1 if not found.
*/
qint64 findParentId(qint64 searchId) const;

private:
QHash<qint64, QString> m_items;
QHash<qint64, QList<QList<qint64> > > m_childItems;
qint64 nextId;
qint64 newId() { return nextId++; };

QModelIndex m_nextParentIndex;
int m_nextRow;

int m_depth;
int maxDepth;

friend class ModelInsertCommand;
friend class ModelMoveCommand;
friend class ModelResetCommand;
friend class ModelResetCommandFixed;

};


class ModelChangeCommand : public QObject
{
Q_OBJECT
public:

ModelChangeCommand( DynamicTreeModel *model, QObject *parent = 0 );

virtual ~ModelChangeCommand() {}

void setAncestorRowNumbers(QList<int> rowNumbers) { m_rowNumbers = rowNumbers; }

QModelIndex findIndex(QList<int> rows);

void setStartRow(int row) { m_startRow = row; }

void setEndRow(int row) { m_endRow = row; }

void setNumCols(int cols) { m_numCols = cols; }

virtual void doCommand() = 0;

protected:
DynamicTreeModel* m_model;
QList<int> m_rowNumbers;
int m_numCols;
int m_startRow;
int m_endRow;

};

typedef QList<ModelChangeCommand*> ModelChangeCommandList;

class ModelInsertCommand : public ModelChangeCommand
{
Q_OBJECT

public:

ModelInsertCommand(DynamicTreeModel *model, QObject *parent = 0 );
virtual ~ModelInsertCommand() {}

virtual void doCommand();
};


class ModelMoveCommand : public ModelChangeCommand
{
Q_OBJECT
public:
ModelMoveCommand(DynamicTreeModel *model, QObject *parent);

virtual ~ModelMoveCommand() {}

virtual bool emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow);

virtual void doCommand();

virtual void emitPostSignal();

void setDestAncestors( QList<int> rows ) { m_destRowNumbers = rows; }

void setDestRow(int row) { m_destRow = row; }

protected:
QList<int> m_destRowNumbers;
int m_destRow;
};

/**
A command which does a move and emits a reset signal.
*/
class ModelResetCommand : public ModelMoveCommand
{
Q_OBJECT
public:
ModelResetCommand(DynamicTreeModel* model, QObject* parent = 0);

virtual ~ModelResetCommand();

virtual bool emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow);
virtual void emitPostSignal();

};

/**
A command which does a move and emits a beginResetModel and endResetModel signals.
*/
class ModelResetCommandFixed : public ModelMoveCommand
{
Q_OBJECT
public:
ModelResetCommandFixed(DynamicTreeModel* model, QObject* parent = 0);

virtual ~ModelResetCommandFixed();

virtual bool emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow);
virtual void emitPostSignal();

};


#endif
566 changes: 566 additions & 0 deletions tests/qt_modeltest/modeltest.cpp

Large diffs are not rendered by default.

94 changes: 94 additions & 0 deletions tests/qt_modeltest/modeltest.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/****************************************************************************
**
** Copyright (C) 2011 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$
** GNU Lesser General Public License Usage
** 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.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 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 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/


#ifndef MODELTEST_H
#define MODELTEST_H

#include <QtCore/QObject>
#include <QtCore/QAbstractItemModel>
#include <QtCore/QStack>

class ModelTest : public QObject
{
Q_OBJECT

public:
ModelTest( QAbstractItemModel *model, QObject *parent = 0 );

private Q_SLOTS:
void nonDestructiveBasicTest();
void rowCount();
void columnCount();
void hasIndex();
void index();
void parent();
void data();

protected Q_SLOTS:
void runAllTests();
void layoutAboutToBeChanged();
void layoutChanged();
void rowsAboutToBeInserted( const QModelIndex &parent, int start, int end );
void rowsInserted( const QModelIndex & parent, int start, int end );
void rowsAboutToBeRemoved( const QModelIndex &parent, int start, int end );
void rowsRemoved( const QModelIndex & parent, int start, int end );

private:
void checkChildren( const QModelIndex &parent, int currentDepth = 0 );

QAbstractItemModel *model;

struct Changing {
QModelIndex parent;
int oldSize;
QVariant last;
QVariant next;
};
QStack<Changing> insert;
QStack<Changing> remove;

bool fetchingMore;

QList<QPersistentModelIndex> changing;
};

#endif
7 changes: 7 additions & 0 deletions tests/qt_modeltest/readme.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Model Test
----------

This is a simple tool from Qt for checking various errors of custom
implementations of models for item views.

http://developer.qt.nokia.com/wiki/Model_Test
309 changes: 309 additions & 0 deletions tests/qt_modeltest/tst_modeltest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,309 @@
/****************************************************************************
**
** Copyright (C) 2011 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$
** GNU Lesser General Public License Usage
** 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.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 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 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/


#include <QtTest/QtTest>
#include <QtGui/QtGui>

#include "modeltest.h"
#include "dynamictreemodel.h"


class tst_ModelTest : public QObject
{
Q_OBJECT

public:
tst_ModelTest() {}
virtual ~tst_ModelTest() {}

public slots:
void initTestCase();
void cleanupTestCase();
void init();
void cleanup();

private slots:
void stringListModel();
void treeWidgetModel();
void standardItemModel();
void testInsertThroughProxy();
void moveSourceItems();
void testResetThroughProxy();
};



void tst_ModelTest::initTestCase()
{
}

void tst_ModelTest::cleanupTestCase()
{
}

void tst_ModelTest::init()
{

}

void tst_ModelTest::cleanup()
{
}
/*
tests
*/

void tst_ModelTest::stringListModel()
{
QStringListModel model;
QSortFilterProxyModel proxy;

ModelTest t1(&model);
ModelTest t2(&proxy);

proxy.setSourceModel(&model);

model.setStringList(QStringList() << "2" << "3" << "1");
model.setStringList(QStringList() << "a" << "e" << "plop" << "b" << "c" );

proxy.setDynamicSortFilter(true);
proxy.setFilterRegExp(QRegExp("[^b]"));
}

void tst_ModelTest::treeWidgetModel()
{
QTreeWidget widget;

ModelTest t1(widget.model());

QTreeWidgetItem *root = new QTreeWidgetItem(&widget, QStringList("root"));
for (int i = 0; i < 20; ++i) {
new QTreeWidgetItem(root, QStringList(QString::number(i)));
}
QTreeWidgetItem *remove = root->child(2);
root->removeChild(remove);
QTreeWidgetItem *parent = new QTreeWidgetItem(&widget, QStringList("parent"));
new QTreeWidgetItem(parent, QStringList("child"));
widget.setItemHidden(parent, true);

widget.sortByColumn(0);
}

void tst_ModelTest::standardItemModel()
{
QStandardItemModel model(10,10);
QSortFilterProxyModel proxy;


ModelTest t1(&model);
ModelTest t2(&proxy);

proxy.setSourceModel(&model);

model.insertRows(2, 5);
model.removeRows(4, 5);

model.insertColumns(2, 5);
model.removeColumns(4, 5);

model.insertRows(0,5, model.index(1,1));
model.insertColumns(0,5, model.index(1,3));
}

void tst_ModelTest::testInsertThroughProxy()
{
DynamicTreeModel *model = new DynamicTreeModel(this);

QSortFilterProxyModel *proxy = new QSortFilterProxyModel(this);
proxy->setSourceModel(model);

new ModelTest(proxy, this);

ModelInsertCommand *insertCommand = new ModelInsertCommand(model, this);
insertCommand->setNumCols(4);
insertCommand->setStartRow(0);
insertCommand->setEndRow(9);
// Parent is QModelIndex()
insertCommand->doCommand();

insertCommand = new ModelInsertCommand(model, this);
insertCommand->setNumCols(4);
insertCommand->setAncestorRowNumbers(QList<int>() << 5);
insertCommand->setStartRow(0);
insertCommand->setEndRow(9);
insertCommand->doCommand();

ModelMoveCommand *moveCommand = new ModelMoveCommand(model, this);
moveCommand->setNumCols(4);
moveCommand->setStartRow(0);
moveCommand->setEndRow(0);
moveCommand->setDestRow(9);
moveCommand->setDestAncestors(QList<int>() << 5);
moveCommand->doCommand();
}

/**
Makes the persistent index list publicly accessible
*/
class AccessibleProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
AccessibleProxyModel(QObject *parent = 0) : QSortFilterProxyModel(parent) {}

QModelIndexList persistent()
{
return persistentIndexList();
}
};

class ObservingObject : public QObject
{
Q_OBJECT
public:
ObservingObject(AccessibleProxyModel *proxy, QObject *parent = 0)
: QObject(parent),
m_proxy(proxy)
{
connect(m_proxy, SIGNAL(layoutAboutToBeChanged()), SLOT(storePersistent()));
connect(m_proxy, SIGNAL(layoutChanged()), SLOT(checkPersistent()));
}

public slots:

void storePersistent(const QModelIndex &parent)
{
for (int row = 0; row < m_proxy->rowCount(parent); ++row) {
QModelIndex proxyIndex = m_proxy->index(row, 0, parent);
QModelIndex sourceIndex = m_proxy->mapToSource(proxyIndex);
Q_ASSERT(proxyIndex.isValid());
Q_ASSERT(sourceIndex.isValid());
m_persistentSourceIndexes.append(sourceIndex);
m_persistentProxyIndexes.append(proxyIndex);
if (m_proxy->hasChildren(proxyIndex))
storePersistent(proxyIndex);
}
}

void storePersistent()
{
foreach(const QModelIndex &idx, m_persistentProxyIndexes)
Q_ASSERT(idx.isValid()); // This is called from layoutAboutToBeChanged. Persistent indexes should be valid

Q_ASSERT(m_proxy->persistent().isEmpty());
storePersistent(QModelIndex());
Q_ASSERT(!m_proxy->persistent().isEmpty());
}

void checkPersistent()
{
for (int row = 0; row < m_persistentProxyIndexes.size(); ++row) {
QModelIndex updatedProxy = m_persistentProxyIndexes.at(row);
QModelIndex updatedSource = m_persistentSourceIndexes.at(row);
}
for (int row = 0; row < m_persistentProxyIndexes.size(); ++row) {
QModelIndex updatedProxy = m_persistentProxyIndexes.at(row);
QModelIndex updatedSource = m_persistentSourceIndexes.at(row);
QCOMPARE(m_proxy->mapToSource(updatedProxy), updatedSource);
}
m_persistentSourceIndexes.clear();
m_persistentProxyIndexes.clear();
}

private:
AccessibleProxyModel *m_proxy;
QList<QPersistentModelIndex> m_persistentSourceIndexes;
QList<QPersistentModelIndex> m_persistentProxyIndexes;
};

void tst_ModelTest::moveSourceItems()
{
DynamicTreeModel *model = new DynamicTreeModel(this);
AccessibleProxyModel *proxy = new AccessibleProxyModel(this);
proxy->setSourceModel(model);

ModelInsertCommand *insertCommand = new ModelInsertCommand(model, this);
insertCommand->setStartRow(0);
insertCommand->setEndRow(2);
insertCommand->doCommand();

insertCommand = new ModelInsertCommand(model, this);
insertCommand->setAncestorRowNumbers(QList<int>() << 1);
insertCommand->setStartRow(0);
insertCommand->setEndRow(2);
insertCommand->doCommand();

ObservingObject observer(proxy);

ModelMoveCommand *moveCommand = new ModelMoveCommand(model, this);
moveCommand->setStartRow(0);
moveCommand->setEndRow(0);
moveCommand->setDestAncestors(QList<int>() << 1);
moveCommand->setDestRow(0);
moveCommand->doCommand();
}

void tst_ModelTest::testResetThroughProxy()
{
DynamicTreeModel *model = new DynamicTreeModel(this);

ModelInsertCommand *insertCommand = new ModelInsertCommand(model, this);
insertCommand->setStartRow(0);
insertCommand->setEndRow(2);
insertCommand->doCommand();

QPersistentModelIndex persistent = model->index(0, 0);

AccessibleProxyModel *proxy = new AccessibleProxyModel(this);
proxy->setSourceModel(model);

ObservingObject observer(proxy);
observer.storePersistent();

ModelResetCommand *resetCommand = new ModelResetCommand(model, this);
resetCommand->setNumCols(0);
resetCommand->doCommand();
}


QTEST_MAIN(tst_ModelTest)
#include "tst_modeltest.moc"