summaryrefslogtreecommitdiffstats
path: root/tests/auto/corelib
diff options
context:
space:
mode:
authorHolger Ihrig <holger.ihrig@nokia.com>2011-08-25 12:16:22 +0200
committerHolger Ihrig <holger.ihrig@nokia.com>2011-09-01 12:42:14 +0200
commit3a7e6157d1a9865a31a5641605bbe9b0fc25e1d4 (patch)
tree0fbbaff4f42115383f08aef07baa58e841ba551a /tests/auto/corelib
parenta90f50942e5304e6bf1c8a3e32f1f65c7a38f60b (diff)
Moving relevant tests to corelib/kernel
Added Test for qmetaproperty Marked QSocketNotifier Autotest as insignificant. See QTBUG-21204 Marked qtranslator Autotest as insignificant. See QTBUG-21125 Marked corelib/io/qfile/largefile as insignificant. See QTBUG-21175 Task-number: QTBUG-21066 Change-Id: I2a7f6587845c355091bb07c8dd3d1557d16db0be Reviewed-on: http://codereview.qt.nokia.com/3598 Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com> Reviewed-by: Sergio Ahumada <sergio.ahumada@nokia.com>
Diffstat (limited to 'tests/auto/corelib')
-rw-r--r--tests/auto/corelib/corelib.pro3
-rw-r--r--tests/auto/corelib/io/qfile/largefile/largefile.pro1
-rw-r--r--tests/auto/corelib/kernel/kernel.pro22
-rw-r--r--tests/auto/corelib/kernel/qabstractitemmodel/.gitignore1
-rw-r--r--tests/auto/corelib/kernel/qabstractitemmodel/qabstractitemmodel.pro9
-rw-r--r--tests/auto/corelib/kernel/qabstractitemmodel/tst_qabstractitemmodel.cpp1816
-rw-r--r--tests/auto/corelib/kernel/qcoreapplication/.gitignore1
-rw-r--r--tests/auto/corelib/kernel/qcoreapplication/qcoreapplication.pro4
-rw-r--r--tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp582
-rw-r--r--tests/auto/corelib/kernel/qeventloop/.gitignore1
-rw-r--r--tests/auto/corelib/kernel/qeventloop/qeventloop.pro8
-rw-r--r--tests/auto/corelib/kernel/qeventloop/tst_qeventloop.cpp937
-rw-r--r--tests/auto/corelib/kernel/qitemmodel/.gitignore1
-rw-r--r--tests/auto/corelib/kernel/qitemmodel/README3
-rw-r--r--tests/auto/corelib/kernel/qitemmodel/modelstotest.cpp424
-rw-r--r--tests/auto/corelib/kernel/qitemmodel/qitemmodel.pro26
-rw-r--r--tests/auto/corelib/kernel/qitemmodel/tst_qitemmodel.cpp1410
-rw-r--r--tests/auto/corelib/kernel/qmath/qmath.pro7
-rw-r--r--tests/auto/corelib/kernel/qmath/tst_qmath.cpp67
-rw-r--r--tests/auto/corelib/kernel/qmetaobject/.gitignore1
-rw-r--r--tests/auto/corelib/kernel/qmetaobject/qmetaobject.pro8
-rw-r--r--tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp1120
-rw-r--r--tests/auto/corelib/kernel/qmetaproperty/qmetaproperty.pro4
-rw-r--r--tests/auto/corelib/kernel/qmetaproperty/tst_qmetaproperty.cpp115
-rw-r--r--tests/auto/corelib/kernel/qmetatype/.gitignore1
-rw-r--r--tests/auto/corelib/kernel/qmetatype/qmetatype.pro4
-rw-r--r--tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp365
-rw-r--r--tests/auto/corelib/kernel/qmimedata/qmimedata.pro4
-rw-r--r--tests/auto/corelib/kernel/qmimedata/tst_qmimedata.cpp343
-rw-r--r--tests/auto/corelib/kernel/qobject/.gitignore3
-rw-r--r--tests/auto/corelib/kernel/qobject/moc_oldnormalizeobject.cpp154
-rw-r--r--tests/auto/corelib/kernel/qobject/oldnormalizeobject.h69
-rw-r--r--tests/auto/corelib/kernel/qobject/qobject.pro3
-rw-r--r--tests/auto/corelib/kernel/qobject/signalbug.cpp154
-rw-r--r--tests/auto/corelib/kernel/qobject/signalbug.h103
-rw-r--r--tests/auto/corelib/kernel/qobject/signalbug.pro19
-rw-r--r--tests/auto/corelib/kernel/qobject/tst_qobject.cpp4065
-rw-r--r--tests/auto/corelib/kernel/qobject/tst_qobject.pro21
-rw-r--r--tests/auto/corelib/kernel/qpointer/.gitignore1
-rw-r--r--tests/auto/corelib/kernel/qpointer/qpointer.pro5
-rw-r--r--tests/auto/corelib/kernel/qpointer/tst_qpointer.cpp343
-rw-r--r--tests/auto/corelib/kernel/qsignalmapper/.gitignore1
-rw-r--r--tests/auto/corelib/kernel/qsignalmapper/qsignalmapper.pro6
-rw-r--r--tests/auto/corelib/kernel/qsignalmapper/tst_qsignalmapper.cpp156
-rw-r--r--tests/auto/corelib/kernel/qsocketnotifier/.gitignore1
-rw-r--r--tests/auto/corelib/kernel/qsocketnotifier/qsocketnotifier.pro11
-rw-r--r--tests/auto/corelib/kernel/qsocketnotifier/tst_qsocketnotifier.cpp367
-rw-r--r--tests/auto/corelib/kernel/qtimer/.gitignore1
-rw-r--r--tests/auto/corelib/kernel/qtimer/qtimer.pro5
-rw-r--r--tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp794
-rw-r--r--tests/auto/corelib/kernel/qtipc/lackey/lackey.pro16
-rw-r--r--tests/auto/corelib/kernel/qtipc/lackey/main.cpp370
-rw-r--r--tests/auto/corelib/kernel/qtipc/lackey/scripts/consumer.js41
-rw-r--r--tests/auto/corelib/kernel/qtipc/lackey/scripts/producer.js44
-rw-r--r--tests/auto/corelib/kernel/qtipc/lackey/scripts/readonly_segfault.js4
-rw-r--r--tests/auto/corelib/kernel/qtipc/lackey/scripts/systemlock_read.js11
-rw-r--r--tests/auto/corelib/kernel/qtipc/lackey/scripts/systemlock_readwrite.js11
-rw-r--r--tests/auto/corelib/kernel/qtipc/lackey/scripts/systemsemaphore_acquire.js18
-rw-r--r--tests/auto/corelib/kernel/qtipc/lackey/scripts/systemsemaphore_acquirerelease.js11
-rw-r--r--tests/auto/corelib/kernel/qtipc/lackey/scripts/systemsemaphore_release.js11
-rw-r--r--tests/auto/corelib/kernel/qtipc/qsharedmemory/.gitignore3
-rw-r--r--tests/auto/corelib/kernel/qtipc/qsharedmemory/qsharedmemory.pro4
-rw-r--r--tests/auto/corelib/kernel/qtipc/qsharedmemory/qsystemlock/qsystemlock.pro22
-rw-r--r--tests/auto/corelib/kernel/qtipc/qsharedmemory/qsystemlock/tst_qsystemlock.cpp239
-rw-r--r--tests/auto/corelib/kernel/qtipc/qsharedmemory/src/qsystemlock.cpp246
-rw-r--r--tests/auto/corelib/kernel/qtipc/qsharedmemory/src/qsystemlock.h135
-rw-r--r--tests/auto/corelib/kernel/qtipc/qsharedmemory/src/qsystemlock_p.h109
-rw-r--r--tests/auto/corelib/kernel/qtipc/qsharedmemory/src/qsystemlock_unix.cpp234
-rw-r--r--tests/auto/corelib/kernel/qtipc/qsharedmemory/src/qsystemlock_win.cpp183
-rw-r--r--tests/auto/corelib/kernel/qtipc/qsharedmemory/src/src.pri11
-rw-r--r--tests/auto/corelib/kernel/qtipc/qsharedmemory/test/test.pro37
-rw-r--r--tests/auto/corelib/kernel/qtipc/qsharedmemory/tst_qsharedmemory.cpp836
-rw-r--r--tests/auto/corelib/kernel/qtipc/qsystemsemaphore/.gitignore1
-rw-r--r--tests/auto/corelib/kernel/qtipc/qsystemsemaphore/files.qrc7
-rw-r--r--tests/auto/corelib/kernel/qtipc/qsystemsemaphore/qsystemsemaphore.pro38
-rw-r--r--tests/auto/corelib/kernel/qtipc/qsystemsemaphore/tst_qsystemsemaphore.cpp319
-rw-r--r--tests/auto/corelib/kernel/qtipc/qtipc.pro6
-rw-r--r--tests/auto/corelib/kernel/qtranslator/.gitignore1
-rw-r--r--tests/auto/corelib/kernel/qtranslator/hellotr_la.qmbin0 -> 230 bytes
-rw-r--r--tests/auto/corelib/kernel/qtranslator/hellotr_la.ts16
-rw-r--r--tests/auto/corelib/kernel/qtranslator/msgfmt_from_po.qmbin0 -> 176988 bytes
-rw-r--r--tests/auto/corelib/kernel/qtranslator/qtranslator.pro12
-rw-r--r--tests/auto/corelib/kernel/qtranslator/qtranslator.qrc5
-rw-r--r--tests/auto/corelib/kernel/qtranslator/tst_qtranslator.cpp260
-rw-r--r--tests/auto/corelib/kernel/qvariant/.gitignore1
-rw-r--r--tests/auto/corelib/kernel/qvariant/qvariant.pro5
-rw-r--r--tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp3235
-rw-r--r--tests/auto/corelib/kernel/qwineventnotifier/.gitignore1
-rw-r--r--tests/auto/corelib/kernel/qwineventnotifier/qwineventnotifier.pro4
-rw-r--r--tests/auto/corelib/kernel/qwineventnotifier/tst_qwineventnotifier.cpp146
90 files changed, 20222 insertions, 1 deletions
diff --git a/tests/auto/corelib/corelib.pro b/tests/auto/corelib/corelib.pro
index 2533d714a0..5764ccc3e1 100644
--- a/tests/auto/corelib/corelib.pro
+++ b/tests/auto/corelib/corelib.pro
@@ -4,4 +4,5 @@ SUBDIRS=\
codecs \
concurrent \
global \
- io
+ io \
+ kernel
diff --git a/tests/auto/corelib/io/qfile/largefile/largefile.pro b/tests/auto/corelib/io/qfile/largefile/largefile.pro
index 6407cb6b3e..7786bdb892 100644
--- a/tests/auto/corelib/io/qfile/largefile/largefile.pro
+++ b/tests/auto/corelib/io/qfile/largefile/largefile.pro
@@ -6,3 +6,4 @@ SOURCES += tst_largefile.cpp
wince*: SOURCES += $$QT_SOURCE_TREE/src/corelib/kernel/qfunctions_wince.cpp
CONFIG += parallel_test
+CONFIG += insignificant_test # QTBUG-21175
diff --git a/tests/auto/corelib/kernel/kernel.pro b/tests/auto/corelib/kernel/kernel.pro
new file mode 100644
index 0000000000..9fb8868391
--- /dev/null
+++ b/tests/auto/corelib/kernel/kernel.pro
@@ -0,0 +1,22 @@
+TEMPLATE=subdirs
+SUBDIRS=\
+ qabstractitemmodel \
+ qcoreapplication \
+ qeventloop \
+ qitemmodel \
+ qmath \
+ qmetaobject \
+ qmetatype \
+ qmimedata \
+ qobject \
+ qpointer \
+ qsignalmapper \
+ qsocketnotifier \
+ qtimer \
+ # qtipc \ # needs to be moved to qtscript
+ qtranslator \
+ qvariant \
+ qwineventnotifier
+
+!contains(QT_CONFIG, private_tests): SUBDIRS -= \
+ qsocketnotifier
diff --git a/tests/auto/corelib/kernel/qabstractitemmodel/.gitignore b/tests/auto/corelib/kernel/qabstractitemmodel/.gitignore
new file mode 100644
index 0000000000..f6f93f4b89
--- /dev/null
+++ b/tests/auto/corelib/kernel/qabstractitemmodel/.gitignore
@@ -0,0 +1 @@
+tst_qabstractitemmodel
diff --git a/tests/auto/corelib/kernel/qabstractitemmodel/qabstractitemmodel.pro b/tests/auto/corelib/kernel/qabstractitemmodel/qabstractitemmodel.pro
new file mode 100644
index 0000000000..50730b371f
--- /dev/null
+++ b/tests/auto/corelib/kernel/qabstractitemmodel/qabstractitemmodel.pro
@@ -0,0 +1,9 @@
+load(qttest_p4)
+
+INCLUDEPATH += $$PWD/../../../modeltest
+QT += widgets
+SOURCES += tst_qabstractitemmodel.cpp ../../../modeltest/dynamictreemodel.cpp ../../../modeltest/modeltest.cpp
+HEADERS += ../../../modeltest/dynamictreemodel.h ../../../modeltest/modeltest.h
+
+
+
diff --git a/tests/auto/corelib/kernel/qabstractitemmodel/tst_qabstractitemmodel.cpp b/tests/auto/corelib/kernel/qabstractitemmodel/tst_qabstractitemmodel.cpp
new file mode 100644
index 0000000000..6a7a16eae8
--- /dev/null
+++ b/tests/auto/corelib/kernel/qabstractitemmodel/tst_qabstractitemmodel.cpp
@@ -0,0 +1,1816 @@
+/****************************************************************************
+**
+** 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 <QtCore/QtCore>
+
+#include <QtWidgets/QSortFilterProxyModel>
+
+//TESTED_CLASS=QAbstractListModel QAbstractTableModel
+//TESTED_FILES=
+
+#include "dynamictreemodel.h"
+
+Q_DECLARE_METATYPE(QModelIndex)
+
+/*!
+ Note that this doesn't test models, but any functionality that QAbstractItemModel shoudl provide
+ */
+class tst_QAbstractItemModel : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QAbstractItemModel();
+ virtual ~tst_QAbstractItemModel();
+
+public slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+
+private slots:
+ void index();
+ void parent();
+ void hasChildren();
+ void _data();
+ void headerData();
+ void itemData();
+ void itemFlags();
+ void match();
+ void dropMimeData_data();
+ void dropMimeData();
+ void changePersistentIndex();
+ void movePersistentIndex();
+
+ void insertRows();
+ void insertColumns();
+ void removeRows();
+ void removeColumns();
+
+ void reset();
+
+ void complexChangesWithPersistent();
+
+ void testMoveSameParentUp_data();
+ void testMoveSameParentUp();
+
+ void testMoveSameParentDown_data();
+ void testMoveSameParentDown();
+
+ void testMoveToGrandParent_data();
+ void testMoveToGrandParent();
+
+ void testMoveToSibling_data();
+ void testMoveToSibling();
+
+ void testMoveToUncle_data();
+ void testMoveToUncle();
+
+ void testMoveToDescendants();
+
+ void testMoveWithinOwnRange_data();
+ void testMoveWithinOwnRange();
+
+ void testMoveThroughProxy();
+
+ void testReset();
+
+
+private:
+ DynamicTreeModel *m_model;
+
+};
+
+/*!
+ Test model that impliments the pure vitual functions and anything else that is
+ needed.
+
+ It is a table implimented as a vector of vectors of strings.
+ */
+class QtTestModel: public QAbstractItemModel
+{
+public:
+ QtTestModel(int rows, int columns, QObject *parent = 0);
+ QtTestModel(const QVector<QVector<QString> > tbl, QObject *parent = 0);
+ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
+ QModelIndex parent(const QModelIndex &) const;
+ int rowCount(const QModelIndex &parent) const;
+ int columnCount(const QModelIndex &parent) const;
+ bool hasChildren(const QModelIndex &) const;
+ QVariant data(const QModelIndex &idx, int) const;
+ bool setData(const QModelIndex &idx, const QVariant &value, int);
+ bool insertRows(int row, int count, const QModelIndex &parent= QModelIndex());
+ bool insertColumns(int column, int count, const QModelIndex &parent= QModelIndex());
+ void setPersistent(const QModelIndex &from, const QModelIndex &to);
+ bool removeRows ( int row, int count, const QModelIndex & parent = QModelIndex() );
+ bool removeColumns( int column, int count, const QModelIndex & parent = QModelIndex());
+ void reset();
+
+ int cCount, rCount;
+ mutable bool wrongIndex;
+ QVector<QVector<QString> > table;
+};
+
+QtTestModel::QtTestModel(int rows, int columns, QObject *parent)
+ : QAbstractItemModel(parent), cCount(columns), rCount(rows), wrongIndex(false) {
+
+ table.resize(rows);
+ for (int r = 0; r < rows; ++r) {
+ table[r].resize(columns);
+ for (int c = 0; c < columns; ++c)
+ table[r][c] = QString("%1/%2").arg(r).arg(c);
+ }
+}
+
+QtTestModel::QtTestModel(const QVector<QVector<QString> > tbl, QObject *parent)
+ : QAbstractItemModel(parent), wrongIndex(false) {
+ table = tbl;
+ rCount = tbl.count();
+ cCount = tbl.at(0).count();
+}
+
+QModelIndex QtTestModel::index(int row, int column, const QModelIndex &parent) const
+ { return hasIndex(row, column, parent) ? createIndex(row, column, 0) : QModelIndex(); }
+
+QModelIndex QtTestModel::parent(const QModelIndex &) const { return QModelIndex(); }
+int QtTestModel::rowCount(const QModelIndex &parent) const { return parent.isValid() ? 0 : rCount; }
+int QtTestModel::columnCount(const QModelIndex &parent) const { return parent.isValid() ? 0 : cCount; }
+bool QtTestModel::hasChildren(const QModelIndex &) const { return false; }
+
+QVariant QtTestModel::data(const QModelIndex &idx, int) const
+{
+ if (idx.row() < 0 || idx.column() < 0 || idx.column() > cCount || idx.row() > rCount) {
+ wrongIndex = true;
+ qWarning("got invalid modelIndex %d/%d", idx.row(), idx.column());
+ return QVariant();
+ }
+ return table.at(idx.row()).at(idx.column());
+}
+
+bool QtTestModel::setData(const QModelIndex &idx, const QVariant &value, int)
+{
+ table[idx.row()][idx.column()] = value.toString();
+ return true;
+}
+
+bool QtTestModel::insertRows(int row, int count, const QModelIndex &parent)
+{
+ QAbstractItemModel::beginInsertRows(parent, row, row + count - 1);
+ int cc = columnCount(parent);
+ table.insert(row, count, QVector<QString>(cc));
+ rCount = table.count();
+ QAbstractItemModel::endInsertRows();
+ return true;
+}
+
+bool QtTestModel::insertColumns(int column, int count, const QModelIndex &parent)
+{
+ QAbstractItemModel::beginInsertColumns(parent, column, column + count - 1);
+ int rc = rowCount(parent);
+ for (int i = 0; i < rc; ++i)
+ table[i].insert(column, 1, "");
+ cCount = table.at(0).count();
+ QAbstractItemModel::endInsertColumns();
+ return true;
+}
+
+void QtTestModel::setPersistent(const QModelIndex &from, const QModelIndex &to)
+{
+ changePersistentIndex(from, to);
+}
+
+bool QtTestModel::removeRows( int row, int count, const QModelIndex & parent)
+{
+ QAbstractItemModel::beginRemoveRows(parent, row, row + count - 1);
+
+ for (int r = row+count-1; r >= row; --r)
+ table.remove(r);
+ rCount = table.count();
+
+ QAbstractItemModel::endRemoveRows();
+ return true;
+}
+
+bool QtTestModel::removeColumns(int column, int count, const QModelIndex & parent)
+{
+ QAbstractItemModel::beginRemoveColumns(parent, column, column + count - 1);
+
+ for (int c = column+count-1; c > column; --c)
+ for (int r = 0; r < rCount; ++r)
+ table[r].remove(c);
+
+ cCount = table.at(0).count();
+
+ QAbstractItemModel::endRemoveColumns();
+ return true;
+}
+
+void QtTestModel::reset()
+{
+ QAbstractItemModel::reset();
+}
+
+tst_QAbstractItemModel::tst_QAbstractItemModel()
+{
+}
+
+tst_QAbstractItemModel::~tst_QAbstractItemModel()
+{
+}
+
+/**
+ * The source Model *must* be initialized before the _data function, since the _data function uses QModelIndexes to reference the items in the tables.
+ * Therefore, we must initialize it globally.
+ */
+
+void tst_QAbstractItemModel::initTestCase()
+{
+ qRegisterMetaType<QModelIndex>("QModelIndex");
+}
+
+void tst_QAbstractItemModel::cleanupTestCase()
+{
+
+}
+
+void tst_QAbstractItemModel::init()
+{
+ m_model = new DynamicTreeModel(this);
+
+ ModelInsertCommand *insertCommand = new ModelInsertCommand(m_model, this);
+ insertCommand->setNumCols(4);
+ insertCommand->setStartRow(0);
+ insertCommand->setEndRow(9);
+ insertCommand->doCommand();
+
+ insertCommand = new ModelInsertCommand(m_model, this);
+ insertCommand->setAncestorRowNumbers(QList<int>() << 5);
+ insertCommand->setNumCols(4);
+ insertCommand->setStartRow(0);
+ insertCommand->setEndRow(9);
+ insertCommand->doCommand();
+}
+
+void tst_QAbstractItemModel::cleanup()
+{
+
+}
+
+/*
+ tests
+*/
+
+void tst_QAbstractItemModel::index()
+{
+ QtTestModel model(1, 1);
+ QModelIndex idx = model.index(0, 0, QModelIndex());
+ QVERIFY(idx.isValid());
+}
+
+void tst_QAbstractItemModel::parent()
+{
+ QtTestModel model(1, 1);
+ QModelIndex idx = model.index(0, 0, QModelIndex());
+ QModelIndex par = model.parent(idx);
+ QVERIFY(!par.isValid());
+}
+
+void tst_QAbstractItemModel::hasChildren()
+{
+ QtTestModel model(1, 1);
+ QModelIndex idx = model.index(0, 0, QModelIndex());
+ QVERIFY(model.hasChildren(idx) == false);
+}
+
+void tst_QAbstractItemModel::_data()
+{
+ QtTestModel model(1, 1);
+ QModelIndex idx = model.index(0, 0, QModelIndex());
+ QVERIFY(idx.isValid());
+ QCOMPARE(model.data(idx, Qt::DisplayRole).toString(), QString("0/0"));
+
+ // Default does nothing
+ QCOMPARE(model.setHeaderData(0, Qt::Horizontal, QVariant(0), 0), false);
+}
+
+void tst_QAbstractItemModel::headerData()
+{
+ QtTestModel model(1, 1);
+ QCOMPARE(model.headerData(0, Qt::Horizontal, Qt::DisplayRole).toString(),
+ QString("1"));
+
+ // Default text alignment for header must be invalid
+ QVERIFY( !model.headerData(0, Qt::Horizontal, Qt::TextAlignmentRole).isValid() );
+}
+
+void tst_QAbstractItemModel::itemData()
+{
+ QtTestModel model(1, 1);
+ QModelIndex idx = model.index(0, 0, QModelIndex());
+ QVERIFY(idx.isValid());
+ QMap<int, QVariant> dat = model.itemData(idx);
+ QCOMPARE(dat.count(Qt::DisplayRole), 1);
+ QCOMPARE(dat.value(Qt::DisplayRole).toString(), QString("0/0"));
+}
+
+void tst_QAbstractItemModel::itemFlags()
+{
+ QtTestModel model(1, 1);
+ QModelIndex idx = model.index(0, 0, QModelIndex());
+ QVERIFY(idx.isValid());
+ Qt::ItemFlags flags = model.flags(idx);
+ QCOMPARE(Qt::ItemIsSelectable|Qt::ItemIsEnabled, flags);
+}
+
+void tst_QAbstractItemModel::match()
+{
+ QtTestModel model(4, 1);
+ QModelIndex start = model.index(0, 0, QModelIndex());
+ QVERIFY(start.isValid());
+ QModelIndexList res = model.match(start, Qt::DisplayRole, QVariant("1"), 3);
+ QCOMPARE(res.count(), 1);
+ QModelIndex idx = model.index(1, 0, QModelIndex());
+ bool areEqual = (idx == res.first());
+ QVERIFY(areEqual);
+
+ model.setData(model.index(0, 0, QModelIndex()), "bat", Qt::DisplayRole);
+ model.setData(model.index(1, 0, QModelIndex()), "cat", Qt::DisplayRole);
+ model.setData(model.index(2, 0, QModelIndex()), "dog", Qt::DisplayRole);
+ model.setData(model.index(3, 0, QModelIndex()), "boar", Qt::DisplayRole);
+
+ res = model.match(start, Qt::DisplayRole, QVariant("dog"), -1, Qt::MatchExactly);
+ QCOMPARE(res.count(), 1);
+ res = model.match(start, Qt::DisplayRole, QVariant("a"), -1, Qt::MatchContains);
+ QCOMPARE(res.count(), 3);
+ res = model.match(start, Qt::DisplayRole, QVariant("b"), -1, Qt::MatchStartsWith);
+ QCOMPARE(res.count(), 2);
+ res = model.match(start, Qt::DisplayRole, QVariant("t"), -1, Qt::MatchEndsWith);
+ QCOMPARE(res.count(), 2);
+ res = model.match(start, Qt::DisplayRole, QVariant("*a*"), -1, Qt::MatchWildcard);
+ QCOMPARE(res.count(), 3);
+ res = model.match(start, Qt::DisplayRole, QVariant(".*O.*"), -1, Qt::MatchRegExp);
+ QCOMPARE(res.count(), 2);
+ res = model.match(start, Qt::DisplayRole, QVariant(".*O.*"), -1, Qt::MatchRegExp | Qt::MatchCaseSensitive);
+ QCOMPARE(res.count(), 0);
+ res = model.match(start, Qt::DisplayRole, QVariant("BOAR"), -1, Qt::MatchFixedString);
+ QCOMPARE(res.count(), 1);
+ res = model.match(start, Qt::DisplayRole, QVariant("bat"), -1,
+ Qt::MatchFixedString | Qt::MatchCaseSensitive);
+ QCOMPARE(res.count(), 1);
+}
+
+typedef QPair<int, int> Position;
+typedef QVector<QPair<int, int> > Selection;
+typedef QVector<QVector<QString> > StringTable;
+typedef QVector<QString> StringTableRow;
+Q_DECLARE_METATYPE(Position)
+Q_DECLARE_METATYPE(Selection)
+Q_DECLARE_METATYPE(StringTable)
+
+static StringTableRow qStringTableRow(const QString &s1, const QString &s2, const QString &s3)
+{
+ StringTableRow row;
+ row << s1 << s2 << s3;
+ return row;
+}
+
+#ifdef Q_CC_MSVC
+# define STRINGTABLE (StringTable())
+#else
+# define STRINGTABLE StringTable()
+#endif
+
+void tst_QAbstractItemModel::dropMimeData_data()
+{
+ QTest::addColumn<StringTable>("src_table"); // drag source
+ QTest::addColumn<StringTable>("dst_table"); // drop target
+ QTest::addColumn<Selection>("selection"); // dragged items
+ QTest::addColumn<Position>("dst_position"); // drop position
+ QTest::addColumn<StringTable>("res_table"); // expected result
+
+ {
+ QTest::newRow("2x2 dropped at [0, 0]")
+ << (STRINGTABLE // source table
+ << (qStringTableRow("A", "B", "C"))
+ << (qStringTableRow("D", "E", "F")))
+ << (STRINGTABLE // destination table
+ << (qStringTableRow("0", "1", "2"))
+ << (qStringTableRow("3", "4", "5")))
+ << (Selection() // selection
+ << Position(0, 0) << Position(0, 1)
+ << Position(1, 0) << Position(1, 1))
+ << Position(0, 0) // drop position
+ << (STRINGTABLE // resulting table
+ << (qStringTableRow("A", "B", "" ))
+ << (qStringTableRow("D", "E", "" ))
+ << (qStringTableRow("0", "1", "2"))
+ << (qStringTableRow("3", "4", "5")));
+ }
+
+ {
+ QTest::newRow("2x2 dropped at [1, 0]")
+ << (STRINGTABLE // source table
+ << (qStringTableRow("A", "B", "C"))
+ << (qStringTableRow("D", "E", "F")))
+ << (STRINGTABLE // destination table
+ << (qStringTableRow("0", "1", "2"))
+ << (qStringTableRow("3", "4", "5")))
+ << (Selection() // selection
+ << Position(0, 0) << Position(0, 1)
+ << Position(1, 0) << Position(1, 1))
+ << Position(1, 0) // drop position
+ << (STRINGTABLE // resulting table
+ << (qStringTableRow("0", "1", "2"))
+ << (qStringTableRow("A", "B", "" ))
+ << (qStringTableRow("D", "E", "" ))
+ << (qStringTableRow("3", "4", "5")));
+ }
+
+ {
+ QTest::newRow("2x2 dropped at [3, 0]")
+ << (STRINGTABLE // source table
+ << (qStringTableRow("A", "B", "C"))
+ << (qStringTableRow("D", "E", "F")))
+ << (STRINGTABLE // destination table
+ << (qStringTableRow("0", "1", "2"))
+ << (qStringTableRow("3", "4", "5")))
+ << (Selection() // selection
+ << Position(0, 0) << Position(0, 1)
+ << Position(1, 0) << Position(1, 1))
+ << Position(3, 0) // drop position
+ << (STRINGTABLE // resulting table
+ << (qStringTableRow("0", "1", "2"))
+ << (qStringTableRow("3", "4", "5"))
+ << (qStringTableRow("A", "B", "" ))
+ << (qStringTableRow("D", "E", "" )));
+ }
+
+ {
+ QTest::newRow("2x2 dropped at [0, 1]")
+ << (STRINGTABLE // source table
+ << (qStringTableRow("A", "B", "C"))
+ << (qStringTableRow("D", "E", "F")))
+ << (STRINGTABLE // destination table
+ << (qStringTableRow("0", "1", "2"))
+ << (qStringTableRow("3", "4", "5")))
+ << (Selection() // selection
+ << Position(0, 0) << Position(0, 1)
+ << Position(1, 0) << Position(1, 1))
+ << Position(0, 1) // drop position
+ << (STRINGTABLE // resulting table
+ << (qStringTableRow("" , "A", "B"))
+ << (qStringTableRow("" , "D", "E"))
+ << (qStringTableRow("0", "1", "2"))
+ << (qStringTableRow("3", "4", "5")));
+ }
+
+ {
+ QTest::newRow("2x2 dropped at [0, 2] (line break)")
+ << (STRINGTABLE // source table
+ << (qStringTableRow("A", "B", "C"))
+ << (qStringTableRow("D", "E", "F")))
+ << (STRINGTABLE // destination table
+ << (qStringTableRow("0", "1", "2"))
+ << (qStringTableRow("3", "4", "5")))
+ << (Selection() // selection
+ << Position(0, 0) << Position(0, 1)
+ << Position(1, 0) << Position(1, 1))
+ << Position(0, 2) // drop position
+ << (STRINGTABLE // resulting table
+ << (qStringTableRow("" , "" , "A"))
+ << (qStringTableRow("" , "" , "D"))
+ << (qStringTableRow("" , "" , "B"))
+ << (qStringTableRow("" , "" , "E"))
+ << (qStringTableRow("0", "1", "2"))
+ << (qStringTableRow("3", "4", "5")));
+ }
+
+ {
+ QTest::newRow("2x2 dropped at [3, 2] (line break)")
+ << (STRINGTABLE // source table
+ << (qStringTableRow("A", "B", "C"))
+ << (qStringTableRow("D", "E", "F")))
+ << (STRINGTABLE // destination table
+ << (qStringTableRow("0", "1", "2"))
+ << (qStringTableRow("3", "4", "5")))
+ << (Selection() // selection
+ << Position(0, 0) << Position(0, 1)
+ << Position(1, 0) << Position(1, 1))
+ << Position(3, 2) // drop position
+ << (STRINGTABLE // resulting table
+ << (qStringTableRow("0", "1", "2"))
+ << (qStringTableRow("3", "4", "5"))
+ << (qStringTableRow("" , "" , "A"))
+ << (qStringTableRow("" , "" , "D"))
+ << (qStringTableRow("" , "" , "B"))
+ << (qStringTableRow("" , "" , "E")));
+ }
+
+ {
+ QTest::newRow("non-square dropped at [0, 0]")
+ << (STRINGTABLE // source table
+ << (qStringTableRow("A", "B", "C"))
+ << (qStringTableRow("D", "E", "F")))
+ << (STRINGTABLE // destination table
+ << (qStringTableRow("0", "1", "2"))
+ << (qStringTableRow("3", "4", "5")))
+ << (Selection() // selection
+ << Position(0, 0) << Position(0, 1)
+ << Position(1, 0))
+ << Position(0, 0) // drop position
+ << (STRINGTABLE // resulting table
+ << (qStringTableRow("A", "B", "" ))
+ << (qStringTableRow("D", "" , "" ))
+ << (qStringTableRow("0", "1", "2"))
+ << (qStringTableRow("3", "4", "5")));
+ }
+
+ {
+ QTest::newRow("non-square dropped at [0, 2]")
+ << (STRINGTABLE // source table
+ << (qStringTableRow("A", "B", "C"))
+ << (qStringTableRow("D", "E", "F")))
+ << (STRINGTABLE // destination table
+ << (qStringTableRow("0", "1", "2"))
+ << (qStringTableRow("3", "4", "5")))
+ << (Selection() // selection
+ << Position(0, 0) << Position(0, 1)
+ << Position(1, 0))
+ << Position(0, 2) // drop position
+ << (STRINGTABLE // resulting table
+ << (qStringTableRow("" , "" , "A"))
+ << (qStringTableRow("" , "" , "D"))
+ << (qStringTableRow("" , "" , "B"))
+ << (qStringTableRow("0", "1", "2"))
+ << (qStringTableRow("3", "4", "5")));
+ }
+
+ {
+ QTest::newRow("2x 1x2 dropped at [0, 0] (duplicates)")
+ << (STRINGTABLE // source table
+ << (qStringTableRow("A", "B", "C"))
+ << (qStringTableRow("D", "E", "F")))
+ << (STRINGTABLE // destination table
+ << (qStringTableRow("0", "1", "2"))
+ << (qStringTableRow("3", "4", "5")))
+ << (Selection() // selection; 2x the same row (to simulate selections in hierarchy)
+ << Position(0, 0) << Position(0, 1)
+ << Position(0, 0) << Position(0, 1))
+ << Position(0, 0) // drop position
+ << (STRINGTABLE // resulting table
+ << (qStringTableRow("A", "B", "" ))
+ << (qStringTableRow("A", "" , "" ))
+ << (qStringTableRow("" , "B", "" )) // ### FIXME: strange behavior, but rare case
+ << (qStringTableRow("0", "1", "2"))
+ << (qStringTableRow("3", "4", "5")));
+ }
+
+ {
+ QTest::newRow("2x 1x2 dropped at [3, 2] (duplicates)")
+ << (STRINGTABLE // source table
+ << (qStringTableRow("A", "B", "C"))
+ << (qStringTableRow("D", "E", "F")))
+ << (STRINGTABLE // destination table
+ << (qStringTableRow("0", "1", "2"))
+ << (qStringTableRow("3", "4", "5")))
+ << (Selection() // selection; 2x the same row (to simulate selections in hierarchy)
+ << Position(0, 0) << Position(0, 1)
+ << Position(0, 0) << Position(0, 1))
+ << Position(3, 2) // drop position
+ << (STRINGTABLE // resulting table
+ << (qStringTableRow("0", "1", "2"))
+ << (qStringTableRow("3", "4", "5"))
+ << (qStringTableRow("" , "" , "A"))
+ << (qStringTableRow("" , "" , "B"))
+ << (qStringTableRow("" , "" , "A"))
+ << (qStringTableRow("" , "" , "B")));
+ }
+ {
+ QTest::newRow("2x 1x2 dropped at [3, 2] (different rows)")
+ << (STRINGTABLE // source table
+ << (qStringTableRow("A", "B", "C"))
+ << (qStringTableRow("D", "E", "F"))
+ << (qStringTableRow("G", "H", "I")))
+ << (STRINGTABLE // destination table
+ << (qStringTableRow("0", "1", "2"))
+ << (qStringTableRow("3", "4", "5")))
+ << (Selection() // selection; 2x the same row (to simulate selections in hierarchy)
+ << Position(0, 0) << Position(0, 1)
+ << Position(2, 0) << Position(2, 1))
+ << Position(2, 1) // drop position
+ << (STRINGTABLE // resulting table
+ << (qStringTableRow("0", "1", "2"))
+ << (qStringTableRow("3", "4", "5"))
+ << (qStringTableRow("" , "A" , "B"))
+ << (qStringTableRow("" , "G" , "H")));
+ }
+
+ {
+ QTest::newRow("2x 1x2 dropped at [3, 2] (different rows, over the edge)")
+ << (STRINGTABLE // source table
+ << (qStringTableRow("A", "B", "C"))
+ << (qStringTableRow("D", "E", "F"))
+ << (qStringTableRow("G", "H", "I")))
+ << (STRINGTABLE // destination table
+ << (qStringTableRow("0", "1", "2"))
+ << (qStringTableRow("3", "4", "5")))
+ << (Selection() // selection; 2x the same row (to simulate selections in hierarchy)
+ << Position(0, 0) << Position(0, 1)
+ << Position(2, 0) << Position(2, 1))
+ << Position(3, 2) // drop position
+ << (STRINGTABLE // resulting table
+ << (qStringTableRow("0", "1", "2"))
+ << (qStringTableRow("3", "4", "5"))
+ << (qStringTableRow("" , "" , "A"))
+ << (qStringTableRow("" , "" , "G"))
+ << (qStringTableRow("" , "" , "B"))
+ << (qStringTableRow("" , "" , "H")));
+ }
+}
+
+void tst_QAbstractItemModel::dropMimeData()
+{
+ QFETCH(StringTable, src_table);
+ QFETCH(StringTable, dst_table);
+ QFETCH(Selection, selection);
+ QFETCH(Position, dst_position);
+ QFETCH(StringTable, res_table);
+
+ QtTestModel src(src_table);
+ QtTestModel dst(dst_table);
+ QtTestModel res(res_table);
+
+// qDebug() << "src" << src.rowCount(QModelIndex()) << src.columnCount(QModelIndex());
+// qDebug() << "dst" << dst.rowCount(QModelIndex()) << dst.columnCount(QModelIndex());
+// qDebug() << "res" << res.rowCount(QModelIndex()) << res.columnCount(QModelIndex());
+
+ // get the mimeData from the "selected" indexes
+ QModelIndexList selectedIndexes;
+ for (int i = 0; i < selection.count(); ++i)
+ selectedIndexes << src.index(selection.at(i).first, selection.at(i).second, QModelIndex());
+ QMimeData *md = src.mimeData(selectedIndexes);
+ // do the drop
+ dst.dropMimeData(md, Qt::CopyAction, dst_position.first, dst_position.second, QModelIndex());
+ delete md;
+
+ // compare to the expected results
+ QCOMPARE(dst.rowCount(QModelIndex()), res.rowCount(QModelIndex()));
+ QCOMPARE(dst.columnCount(QModelIndex()), res.columnCount(QModelIndex()));
+ for (int r = 0; r < dst.rowCount(QModelIndex()); ++r) {
+ for (int c = 0; c < dst.columnCount(QModelIndex()); ++c) {
+ QModelIndex dst_idx = dst.index(r, c, QModelIndex());
+ QModelIndex res_idx = res.index(r, c, QModelIndex());
+ QMap<int, QVariant> dst_data = dst.itemData(dst_idx);
+ QMap<int, QVariant> res_data = res.itemData(res_idx);
+ //if(dst_data != res_data)
+ // qDebug() << r << c << dst_data.value(0).toString() << res_data.value(0).toString();
+ QCOMPARE(dst_data , res_data);
+ }
+ }
+
+}
+
+
+void tst_QAbstractItemModel::changePersistentIndex()
+{
+ QtTestModel model(3, 3);
+ QModelIndex a = model.index(1, 2, QModelIndex());
+ QModelIndex b = model.index(2, 1, QModelIndex());
+ QPersistentModelIndex p(a);
+ QVERIFY(p == a);
+ model.setPersistent(a, b);
+ QVERIFY(p == b);
+}
+
+void tst_QAbstractItemModel::movePersistentIndex()
+{
+ QtTestModel model(3, 3);
+
+ QPersistentModelIndex a = model.index(1, 1);
+ QVERIFY(a.isValid());
+ QCOMPARE(a.row(), 1);
+ QCOMPARE(a.column(), 1);
+
+ model.insertRow(0);
+ QCOMPARE(a.row(), 2);
+
+ model.insertRow(1);
+ QCOMPARE(a.row(), 3);
+
+ model.insertColumn(0);
+ QCOMPARE(a.column(), 2);
+}
+
+void tst_QAbstractItemModel::removeRows()
+{
+ QtTestModel model(10, 10);
+
+ QSignalSpy rowsAboutToBeRemovedSpy(&model, SIGNAL(rowsAboutToBeRemoved( const QModelIndex &, int , int )));
+ QSignalSpy rowsRemovedSpy(&model, SIGNAL(rowsRemoved( const QModelIndex &, int, int )));
+
+ QCOMPARE(model.removeRows(6, 4), true);
+ QCOMPARE(rowsAboutToBeRemovedSpy.count(), 1);
+ QCOMPARE(rowsRemovedSpy.count(), 1);
+}
+
+void tst_QAbstractItemModel::removeColumns()
+{
+ QtTestModel model(10, 10);
+
+ QSignalSpy columnsAboutToBeRemovedSpy(&model, SIGNAL(columnsAboutToBeRemoved( const QModelIndex &, int , int )));
+ QSignalSpy columnsRemovedSpy(&model, SIGNAL(columnsRemoved( const QModelIndex &, int, int )));
+
+ QCOMPARE(model.removeColumns(6, 4), true);
+ QCOMPARE(columnsAboutToBeRemovedSpy.count(), 1);
+ QCOMPARE(columnsRemovedSpy.count(), 1);
+}
+
+void tst_QAbstractItemModel::insertRows()
+{
+ QtTestModel model(10, 10);
+
+ QSignalSpy rowsAboutToBeInsertedSpy(&model, SIGNAL(rowsAboutToBeInserted( const QModelIndex &, int , int )));
+ QSignalSpy rowsInsertedSpy(&model, SIGNAL(rowsInserted( const QModelIndex &, int, int )));
+
+ QCOMPARE(model.insertRows(6, 4), true);
+ QCOMPARE(rowsAboutToBeInsertedSpy.count(), 1);
+ QCOMPARE(rowsInsertedSpy.count(), 1);
+}
+
+void tst_QAbstractItemModel::insertColumns()
+{
+ QtTestModel model(10, 10);
+
+ QSignalSpy columnsAboutToBeInsertedSpy(&model, SIGNAL(columnsAboutToBeInserted( const QModelIndex &, int , int )));
+ QSignalSpy columnsInsertedSpy(&model, SIGNAL(columnsInserted( const QModelIndex &, int, int )));
+
+ QCOMPARE(model.insertColumns(6, 4), true);
+ QCOMPARE(columnsAboutToBeInsertedSpy.count(), 1);
+ QCOMPARE(columnsInsertedSpy.count(), 1);
+}
+
+void tst_QAbstractItemModel::reset()
+{
+ QtTestModel model(10, 10);
+
+ QSignalSpy resetSpy(&model, SIGNAL(modelReset()));
+ model.reset();
+ QCOMPARE(resetSpy.count(), 1);
+}
+
+void tst_QAbstractItemModel::complexChangesWithPersistent()
+{
+ QtTestModel model(10, 10);
+ QPersistentModelIndex a = model.index(1, 1, QModelIndex());
+ QPersistentModelIndex b = model.index(9, 7, QModelIndex());
+ QPersistentModelIndex c = model.index(5, 6, QModelIndex());
+ QPersistentModelIndex d = model.index(3, 9, QModelIndex());
+ QPersistentModelIndex e[10];
+ for (int i=0; i <10 ; i++) {
+ e[i] = model.index(2, i , QModelIndex());
+ }
+
+ QVERIFY(a == model.index(1, 1, QModelIndex()));
+ QVERIFY(b == model.index(9, 7, QModelIndex()));
+ QVERIFY(c == model.index(5, 6, QModelIndex()));
+ QVERIFY(d == model.index(3, 9, QModelIndex()));
+ for (int i=0; i <8 ; i++)
+ QVERIFY(e[i] == model.index(2, i , QModelIndex()));
+
+ //remove a bunch of columns
+ model.removeColumns(2, 4);
+
+ QVERIFY(a == model.index(1, 1, QModelIndex()));
+ QVERIFY(b == model.index(9, 3, QModelIndex()));
+ QVERIFY(c == model.index(5, 2, QModelIndex()));
+ QVERIFY(d == model.index(3, 5, QModelIndex()));
+ for (int i=0; i <2 ; i++)
+ QVERIFY(e[i] == model.index(2, i , QModelIndex()));
+ for (int i=2; i <6 ; i++)
+ QVERIFY(!e[i].isValid());
+ for (int i=6; i <10 ; i++)
+ QVERIFY(e[i] == model.index(2, i-4 , QModelIndex()));
+
+ //move some indexes around
+ model.setPersistent(model.index(1, 1 , QModelIndex()), model.index(9, 3 , QModelIndex()));
+ model.setPersistent(model.index(9, 3 , QModelIndex()), model.index(8, 4 , QModelIndex()));
+
+ QVERIFY(a == model.index(9, 3, QModelIndex()));
+ QVERIFY(b == model.index(8, 4, QModelIndex()));
+ QVERIFY(c == model.index(5, 2, QModelIndex()));
+ QVERIFY(d == model.index(3, 5, QModelIndex()));
+ for (int i=0; i <2 ; i++)
+ QVERIFY(e[i] == model.index(2, i , QModelIndex()));
+ for (int i=2; i <6 ; i++)
+ QVERIFY(!e[i].isValid());
+ for (int i=6; i <10 ; i++)
+ QVERIFY(e[i] == model.index(2, i-4 , QModelIndex()));
+
+ //inserting a bunch of columns
+ model.insertColumns(2, 2);
+ QVERIFY(a == model.index(9, 5, QModelIndex()));
+ QVERIFY(b == model.index(8, 6, QModelIndex()));
+ QVERIFY(c == model.index(5, 4, QModelIndex()));
+ QVERIFY(d == model.index(3, 7, QModelIndex()));
+ for (int i=0; i <2 ; i++)
+ QVERIFY(e[i] == model.index(2, i , QModelIndex()));
+ for (int i=2; i <6 ; i++)
+ QVERIFY(!e[i].isValid());
+ for (int i=6; i <10 ; i++)
+ QVERIFY(e[i] == model.index(2, i-2 , QModelIndex()));
+
+}
+
+void tst_QAbstractItemModel::testMoveSameParentDown_data()
+{
+ QTest::addColumn<int>("startRow");
+ QTest::addColumn<int>("endRow");
+ QTest::addColumn<int>("destRow");
+ // We can't put the actual parent index for the move in here because m_model is not defined until init() is run.
+ QTest::addColumn<bool>("topLevel");
+
+ // Move from the start to the middle
+ QTest::newRow("move01") << 0 << 2 << 8 << true;
+ // Move from the start to the end
+ QTest::newRow("move02") << 0 << 2 << 10 << true;
+ // Move from the middle to the middle
+ QTest::newRow("move03") << 3 << 5 << 8 << true;
+ // Move from the middle to the end
+ QTest::newRow("move04") << 3 << 5 << 10 << true;
+
+ QTest::newRow("move05") << 0 << 2 << 8 << false;
+ QTest::newRow("move06") << 0 << 2 << 10 << false;
+ QTest::newRow("move07") << 3 << 5 << 8 << false;
+ QTest::newRow("move08") << 3 << 5 << 10 << false;
+}
+
+void tst_QAbstractItemModel::testMoveSameParentDown()
+{
+ QFETCH( int, startRow);
+ QFETCH( int, endRow);
+ QFETCH( int, destRow);
+ QFETCH( bool, topLevel);
+
+ QModelIndex moveParent = topLevel ? QModelIndex() : m_model->index(5, 0);
+
+ QList<QPersistentModelIndex> persistentList;
+ QModelIndexList indexList;
+
+ for (int column = 0; column < m_model->columnCount(); ++column)
+ {
+ for (int row= 0; row < m_model->rowCount(); ++row)
+ {
+ QModelIndex idx = m_model->index(row, column);
+ QVERIFY(idx.isValid());
+ indexList << idx;
+ persistentList << QPersistentModelIndex(idx);
+ }
+ }
+
+ QModelIndex parent = m_model->index(5, 0);
+ for (int column = 0; column < m_model->columnCount(); ++column)
+ {
+ for (int row= 0; row < m_model->rowCount(parent); ++row)
+ {
+ QModelIndex idx = m_model->index(row, column, parent);
+ QVERIFY(idx.isValid());
+ indexList << idx;
+ persistentList << QPersistentModelIndex(idx);
+ }
+ }
+
+ QSignalSpy beforeSpy(m_model, SIGNAL(rowsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int)));
+ QSignalSpy afterSpy(m_model, SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)));
+
+ ModelMoveCommand *moveCommand = new ModelMoveCommand(m_model, this);
+ moveCommand->setNumCols(4);
+ if (!topLevel)
+ moveCommand->setAncestorRowNumbers(QList<int>() << 5);
+ moveCommand->setStartRow(startRow);
+ moveCommand->setEndRow(endRow);
+ moveCommand->setDestRow(destRow);
+ if (!topLevel)
+ moveCommand->setDestAncestors(QList<int>() << 5);
+ moveCommand->doCommand();
+
+ QVariantList beforeSignal = beforeSpy.takeAt(0);
+ QVariantList afterSignal = afterSpy.takeAt(0);
+
+ QCOMPARE(beforeSignal.size(), 5);
+ QCOMPARE(beforeSignal.at(0).value<QModelIndex>(), moveParent);
+ QCOMPARE(beforeSignal.at(1).toInt(), startRow);
+ QCOMPARE(beforeSignal.at(2).toInt(), endRow);
+ QCOMPARE(beforeSignal.at(3).value<QModelIndex>(), moveParent);
+ QCOMPARE(beforeSignal.at(4).toInt(), destRow);
+
+ QCOMPARE(afterSignal.size(), 5);
+ QCOMPARE(afterSignal.at(0).value<QModelIndex>(), moveParent);
+ QCOMPARE(afterSignal.at(1).toInt(), startRow);
+ QCOMPARE(afterSignal.at(2).toInt(), endRow);
+ QCOMPARE(afterSignal.at(3).value<QModelIndex>(), moveParent);
+ QCOMPARE(afterSignal.at(4).toInt(), destRow);
+
+ for (int i = 0; i < indexList.size(); i++)
+ {
+ QModelIndex idx = indexList.at(i);
+ QModelIndex persistentIndex = persistentList.at(i);
+ if (idx.parent() == moveParent)
+ {
+ int row = idx.row();
+ if ( row >= startRow)
+ {
+ if (row <= endRow)
+ {
+ QCOMPARE(row + destRow - endRow - 1, persistentIndex.row() );
+ QCOMPARE(idx.column(), persistentIndex.column());
+ QCOMPARE(idx.parent(), persistentIndex.parent());
+ QCOMPARE(idx.model(), persistentIndex.model());
+ } else if ( row < destRow)
+ {
+ QCOMPARE(row - (endRow - startRow + 1), persistentIndex.row() );
+ QCOMPARE(idx.column(), persistentIndex.column());
+ QCOMPARE(idx.parent(), persistentIndex.parent());
+ QCOMPARE(idx.model(), persistentIndex.model());
+ } else
+ {
+ QCOMPARE(idx, persistentIndex);
+ }
+ } else
+ {
+ QCOMPARE(idx, persistentIndex);
+ }
+ } else
+ {
+ QCOMPARE(idx, persistentIndex);
+ }
+ }
+}
+
+void tst_QAbstractItemModel::testMoveSameParentUp_data()
+{
+ QTest::addColumn<int>("startRow");
+ QTest::addColumn<int>("endRow");
+ QTest::addColumn<int>("destRow");
+ QTest::addColumn<bool>("topLevel");
+
+ // Move from the middle to the start
+ QTest::newRow("move01") << 5 << 7 << 0 << true;
+ // Move from the end to the start
+ QTest::newRow("move02") << 8 << 9 << 0 << true;
+ // Move from the middle to the middle
+ QTest::newRow("move03") << 5 << 7 << 2 << true;
+ // Move from the end to the middle
+ QTest::newRow("move04") << 8 << 9 << 5 << true;
+
+ QTest::newRow("move05") << 5 << 7 << 0 << false;
+ QTest::newRow("move06") << 8 << 9 << 0 << false;
+ QTest::newRow("move07") << 5 << 7 << 2 << false;
+ QTest::newRow("move08") << 8 << 9 << 5 << false;
+}
+
+void tst_QAbstractItemModel::testMoveSameParentUp()
+{
+
+ QFETCH( int, startRow);
+ QFETCH( int, endRow);
+ QFETCH( int, destRow);
+ QFETCH( bool, topLevel);
+
+ QModelIndex moveParent = topLevel ? QModelIndex() : m_model->index(5, 0);
+
+ QList<QPersistentModelIndex> persistentList;
+ QModelIndexList indexList;
+
+ for (int column = 0; column < m_model->columnCount(); ++column)
+ {
+ for (int row= 0; row < m_model->rowCount(); ++row)
+ {
+ QModelIndex idx = m_model->index(row, column);
+ QVERIFY(idx.isValid());
+ indexList << idx;
+ persistentList << QPersistentModelIndex(idx);
+ }
+ }
+
+ QModelIndex parent = m_model->index(2, 0);
+ for (int column = 0; column < m_model->columnCount(); ++column)
+ {
+ for (int row= 0; row < m_model->rowCount(parent); ++row)
+ {
+ QModelIndex idx = m_model->index(row, column, parent);
+ QVERIFY(idx.isValid());
+ indexList << idx;
+ persistentList << QPersistentModelIndex(idx);
+ }
+ }
+
+ QSignalSpy beforeSpy(m_model, SIGNAL(rowsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int)));
+ QSignalSpy afterSpy(m_model, SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)));
+
+
+ ModelMoveCommand *moveCommand = new ModelMoveCommand(m_model, this);
+ moveCommand->setNumCols(4);
+ if (!topLevel)
+ moveCommand->setAncestorRowNumbers(QList<int>() << 5);
+ moveCommand->setStartRow(startRow);
+ moveCommand->setEndRow(endRow);
+ moveCommand->setDestRow(destRow);
+ if (!topLevel)
+ moveCommand->setDestAncestors(QList<int>() << 5);
+ moveCommand->doCommand();
+
+ QVariantList beforeSignal = beforeSpy.takeAt(0);
+ QVariantList afterSignal = afterSpy.takeAt(0);
+
+ QCOMPARE(beforeSignal.size(), 5);
+ QCOMPARE(beforeSignal.at(0).value<QModelIndex>(), moveParent);
+ QCOMPARE(beforeSignal.at(1).toInt(), startRow);
+ QCOMPARE(beforeSignal.at(2).toInt(), endRow);
+ QCOMPARE(beforeSignal.at(3).value<QModelIndex>(), moveParent);
+ QCOMPARE(beforeSignal.at(4).toInt(), destRow);
+
+ QCOMPARE(afterSignal.size(), 5);
+ QCOMPARE(afterSignal.at(0).value<QModelIndex>(), moveParent);
+ QCOMPARE(afterSignal.at(1).toInt(), startRow);
+ QCOMPARE(afterSignal.at(2).toInt(), endRow);
+ QCOMPARE(afterSignal.at(3).value<QModelIndex>(), moveParent);
+ QCOMPARE(afterSignal.at(4).toInt(), destRow);
+
+
+ for (int i = 0; i < indexList.size(); i++)
+ {
+ QModelIndex idx = indexList.at(i);
+ QModelIndex persistentIndex = persistentList.at(i);
+ if (idx.parent() == moveParent)
+ {
+ int row = idx.row();
+ if ( row >= destRow)
+ {
+ if (row < startRow)
+ {
+ QCOMPARE(row + endRow - startRow + 1, persistentIndex.row() );
+ QCOMPARE(idx.column(), persistentIndex.column());
+ QCOMPARE(idx.parent(), persistentIndex.parent());
+ QCOMPARE(idx.model(), persistentIndex.model());
+ } else if ( row <= endRow)
+ {
+ QCOMPARE(row + destRow - startRow, persistentIndex.row() );
+ QCOMPARE(idx.column(), persistentIndex.column());
+ QCOMPARE(idx.parent(), persistentIndex.parent());
+ QCOMPARE(idx.model(), persistentIndex.model());
+ } else
+ {
+ QCOMPARE(idx, persistentIndex);
+ }
+ } else
+ {
+ QCOMPARE(idx, persistentIndex);
+ }
+ } else
+ {
+ QCOMPARE(idx, persistentIndex);
+ }
+ }
+}
+
+void tst_QAbstractItemModel::testMoveThroughProxy()
+{
+ QSortFilterProxyModel *proxy = new QSortFilterProxyModel(this);
+ proxy->setSourceModel(m_model);
+
+ QList<QPersistentModelIndex> persistentList;
+
+ persistentList.append(proxy->index(0, 0));
+ persistentList.append(proxy->index(0, 0, proxy->mapFromSource(m_model->index(5, 0))));
+
+ ModelMoveCommand *moveCommand = new ModelMoveCommand(m_model, this);
+ moveCommand->setNumCols(4);
+ moveCommand->setAncestorRowNumbers(QList<int>() << 5);
+ moveCommand->setStartRow(0);
+ moveCommand->setEndRow(0);
+ moveCommand->setDestRow(0);
+ moveCommand->doCommand();
+}
+
+void tst_QAbstractItemModel::testMoveToGrandParent_data()
+{
+ QTest::addColumn<int>("startRow");
+ QTest::addColumn<int>("endRow");
+ QTest::addColumn<int>("destRow");
+
+ // Move from the start to the middle
+ QTest::newRow("move01") << 0 << 2 << 8;
+ // Move from the start to the end
+ QTest::newRow("move02") << 0 << 2 << 10;
+ // Move from the middle to the middle
+ QTest::newRow("move03") << 3 << 5 << 8;
+ // Move from the middle to the end
+ QTest::newRow("move04") << 3 << 5 << 10;
+
+ // Move from the middle to the start
+ QTest::newRow("move05") << 5 << 7 << 0;
+ // Move from the end to the start
+ QTest::newRow("move06") << 8 << 9 << 0;
+ // Move from the middle to the middle
+ QTest::newRow("move07") << 5 << 7 << 2;
+ // Move from the end to the middle
+ QTest::newRow("move08") << 8 << 9 << 5;
+
+ // Moving to the same row in a different parent doesn't confuse things.
+ QTest::newRow("move09") << 8 << 8 << 8;
+
+ // Moving to the row of my parent and its neighbours doesn't confuse things
+ QTest::newRow("move09") << 8 << 8 << 4;
+ QTest::newRow("move10") << 8 << 8 << 5;
+ QTest::newRow("move11") << 8 << 8 << 6;
+
+ // Moving everything from one parent to another
+ QTest::newRow("move12") << 0 << 9 << 10;
+ QTest::newRow("move13") << 0 << 9 << 0;
+}
+
+void tst_QAbstractItemModel::testMoveToGrandParent()
+{
+
+ QFETCH( int, startRow);
+ QFETCH( int, endRow);
+ QFETCH( int, destRow);
+
+ QList<QPersistentModelIndex> persistentList;
+ QModelIndexList indexList;
+ QModelIndexList parentsList;
+
+ for (int column = 0; column < m_model->columnCount(); ++column)
+ {
+ for (int row= 0; row < m_model->rowCount(); ++row)
+ {
+ QModelIndex idx = m_model->index(row, column);
+ QVERIFY(idx.isValid());
+ indexList << idx;
+ parentsList << idx.parent();
+ persistentList << QPersistentModelIndex(idx);
+ }
+ }
+
+ QModelIndex sourceIndex = m_model->index(5, 0);
+ for (int column = 0; column < m_model->columnCount(); ++column)
+ {
+ for (int row= 0; row < m_model->rowCount(sourceIndex); ++row)
+ {
+ QModelIndex idx = m_model->index(row, column, sourceIndex);
+ QVERIFY(idx.isValid());
+ indexList << idx;
+ parentsList << idx.parent();
+ persistentList << QPersistentModelIndex(idx);
+ }
+ }
+
+ QSignalSpy beforeSpy(m_model, SIGNAL(rowsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int)));
+ QSignalSpy afterSpy(m_model, SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)));
+
+ QPersistentModelIndex persistentSource = sourceIndex;
+
+ ModelMoveCommand *moveCommand = new ModelMoveCommand(m_model, this);
+ moveCommand->setAncestorRowNumbers(QList<int>() << 5);
+ moveCommand->setNumCols(4);
+ moveCommand->setStartRow(startRow);
+ moveCommand->setEndRow(endRow);
+ moveCommand->setDestRow(destRow);
+ moveCommand->doCommand();
+
+ QVariantList beforeSignal = beforeSpy.takeAt(0);
+ QVariantList afterSignal = afterSpy.takeAt(0);
+
+ QCOMPARE(beforeSignal.size(), 5);
+ QCOMPARE(beforeSignal.at(0).value<QModelIndex>(), sourceIndex);
+ QCOMPARE(beforeSignal.at(1).toInt(), startRow);
+ QCOMPARE(beforeSignal.at(2).toInt(), endRow);
+ QCOMPARE(beforeSignal.at(3).value<QModelIndex>(), QModelIndex());
+ QCOMPARE(beforeSignal.at(4).toInt(), destRow);
+
+ QCOMPARE(afterSignal.size(), 5);
+ QCOMPARE(afterSignal.at(0).value<QModelIndex>(), static_cast<QModelIndex>(persistentSource));
+ QCOMPARE(afterSignal.at(1).toInt(), startRow);
+ QCOMPARE(afterSignal.at(2).toInt(), endRow);
+ QCOMPARE(afterSignal.at(3).value<QModelIndex>(), QModelIndex());
+ QCOMPARE(afterSignal.at(4).toInt(), destRow);
+
+ for (int i = 0; i < indexList.size(); i++)
+ {
+ QModelIndex idx = indexList.at(i);
+ QModelIndex idxParent = parentsList.at(i);
+ QModelIndex persistentIndex = persistentList.at(i);
+ int row = idx.row();
+ if (idxParent == QModelIndex())
+ {
+ if ( row >= destRow)
+ {
+ QCOMPARE(row + endRow - startRow + 1, persistentIndex.row() );
+ QCOMPARE(idx.column(), persistentIndex.column());
+ QCOMPARE(idxParent, persistentIndex.parent());
+ QCOMPARE(idx.model(), persistentIndex.model());
+ } else
+ {
+ QCOMPARE(idx, persistentIndex);
+ }
+ } else
+ {
+ if (row < startRow)
+ {
+ QCOMPARE(idx, persistentIndex);
+ } else if (row <= endRow)
+ {
+ QCOMPARE(row + destRow - startRow, persistentIndex.row() );
+ QCOMPARE(idx.column(), persistentIndex.column());
+ QCOMPARE(QModelIndex(), persistentIndex.parent());
+ QCOMPARE(idx.model(), persistentIndex.model());
+ } else {
+ QCOMPARE(row - (endRow - startRow + 1), persistentIndex.row() );
+ QCOMPARE(idx.column(), persistentIndex.column());
+
+ if (idxParent.row() >= destRow)
+ {
+ QModelIndex adjustedParent;
+ adjustedParent = idxParent.sibling( idxParent.row() + endRow - startRow + 1, idxParent.column());
+ QCOMPARE(adjustedParent, persistentIndex.parent());
+ } else
+ {
+ QCOMPARE(idxParent, persistentIndex.parent());
+ }
+ QCOMPARE(idx.model(), persistentIndex.model());
+ }
+ }
+ }
+}
+
+void tst_QAbstractItemModel::testMoveToSibling_data()
+{
+ QTest::addColumn<int>("startRow");
+ QTest::addColumn<int>("endRow");
+ QTest::addColumn<int>("destRow");
+
+ // Move from the start to the middle
+ QTest::newRow("move01") << 0 << 2 << 8;
+ // Move from the start to the end
+ QTest::newRow("move02") << 0 << 2 << 10;
+ // Move from the middle to the middle
+ QTest::newRow("move03") << 2 << 4 << 8;
+ // Move from the middle to the end
+ QTest::newRow("move04") << 2 << 4 << 10;
+
+ // Move from the middle to the start
+ QTest::newRow("move05") << 8 << 8 << 0;
+ // Move from the end to the start
+ QTest::newRow("move06") << 8 << 9 << 0;
+ // Move from the middle to the middle
+ QTest::newRow("move07") << 6 << 8 << 2;
+ // Move from the end to the middle
+ QTest::newRow("move08") << 8 << 9 << 5;
+
+ // Moving to the same row in a different parent doesn't confuse things.
+ QTest::newRow("move09") << 8 << 8 << 8;
+
+ // Moving to the row of my target and its neighbours doesn't confuse things
+ QTest::newRow("move09") << 8 << 8 << 4;
+ QTest::newRow("move10") << 8 << 8 << 5;
+ QTest::newRow("move11") << 8 << 8 << 6;
+
+ // Move such that the destination parent no longer valid after the move.
+ // The destination parent is always QMI(5, 0), but after this move the
+ // row count is 5, so (5, 0) (used internally in QAIM) no longer refers to a valid index.
+ QTest::newRow("move12") << 0 << 4 << 0;
+}
+
+void tst_QAbstractItemModel::testMoveToSibling()
+{
+
+ QFETCH( int, startRow);
+ QFETCH( int, endRow);
+ QFETCH( int, destRow);
+
+ QList<QPersistentModelIndex> persistentList;
+ QModelIndexList indexList;
+ QModelIndexList parentsList;
+
+ const int column = 0;
+
+ for (int i= 0; i < m_model->rowCount(); ++i)
+ {
+ QModelIndex idx = m_model->index(i, column);
+ QVERIFY(idx.isValid());
+ indexList << idx;
+ parentsList << idx.parent();
+ persistentList << QPersistentModelIndex(idx);
+ }
+
+ QModelIndex destIndex = m_model->index(5, 0);
+ QModelIndex sourceIndex;
+ for (int i= 0; i < m_model->rowCount(destIndex); ++i)
+ {
+ QModelIndex idx = m_model->index(i, column, destIndex);
+ QVERIFY(idx.isValid());
+ indexList << idx;
+ parentsList << idx.parent();
+ persistentList << QPersistentModelIndex(idx);
+ }
+
+ QSignalSpy beforeSpy(m_model, SIGNAL(rowsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int)));
+ QSignalSpy afterSpy(m_model, SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)));
+
+ QPersistentModelIndex persistentDest = destIndex;
+
+ ModelMoveCommand *moveCommand = new ModelMoveCommand(m_model, this);
+ moveCommand->setNumCols(4);
+ moveCommand->setStartRow(startRow);
+ moveCommand->setEndRow(endRow);
+ moveCommand->setDestAncestors(QList<int>() << 5);
+ moveCommand->setDestRow(destRow);
+ moveCommand->doCommand();
+
+ QVariantList beforeSignal = beforeSpy.takeAt(0);
+ QVariantList afterSignal = afterSpy.takeAt(0);
+
+ QCOMPARE(beforeSignal.size(), 5);
+ QCOMPARE(beforeSignal.at(0).value<QModelIndex>(), sourceIndex);
+ QCOMPARE(beforeSignal.at(1).toInt(), startRow);
+ QCOMPARE(beforeSignal.at(2).toInt(), endRow);
+ QCOMPARE(beforeSignal.at(3).value<QModelIndex>(), destIndex);
+ QCOMPARE(beforeSignal.at(4).toInt(), destRow);
+
+ QCOMPARE(afterSignal.size(), 5);
+ QCOMPARE(afterSignal.at(0).value<QModelIndex>(), sourceIndex);
+ QCOMPARE(afterSignal.at(1).toInt(), startRow);
+ QCOMPARE(afterSignal.at(2).toInt(), endRow);
+ QCOMPARE(afterSignal.at(3).value<QModelIndex>(), static_cast<QModelIndex>(persistentDest));
+ QCOMPARE(afterSignal.at(4).toInt(), destRow);
+
+ for (int i = 0; i < indexList.size(); i++)
+ {
+ QModelIndex idx = indexList.at(i);
+ QModelIndex idxParent = parentsList.at(i);
+ QModelIndex persistentIndex = persistentList.at(i);
+
+ QModelIndex adjustedDestination = destIndex.sibling(destIndex.row() - (endRow - startRow + 1), destIndex.column());
+ int row = idx.row();
+ if (idxParent == destIndex)
+ {
+ if ( row >= destRow)
+ {
+ QCOMPARE(row + endRow - startRow + 1, persistentIndex.row() );
+ QCOMPARE(idx.column(), persistentIndex.column());
+ if (idxParent.row() > startRow)
+ {
+ QCOMPARE(adjustedDestination, persistentIndex.parent());
+ } else {
+ QCOMPARE(destIndex, persistentIndex.parent());
+ }
+ QCOMPARE(idx.model(), persistentIndex.model());
+ } else
+ {
+ QCOMPARE(idx, persistentIndex);
+ }
+ } else
+ {
+ if (row < startRow)
+ {
+ QCOMPARE(idx, persistentIndex);
+ } else if (row <= endRow)
+ {
+ QCOMPARE(row + destRow - startRow, persistentIndex.row() );
+ QCOMPARE(idx.column(), persistentIndex.column());
+ if (destIndex.row() > startRow)
+ {
+ QCOMPARE(adjustedDestination, persistentIndex.parent());
+ } else {
+ QCOMPARE(destIndex, persistentIndex.parent());
+ }
+
+ QCOMPARE(idx.model(), persistentIndex.model());
+
+ } else {
+ QCOMPARE(row - (endRow - startRow + 1), persistentIndex.row() );
+ QCOMPARE(idx.column(), persistentIndex.column());
+ QCOMPARE(idxParent, persistentIndex.parent());
+ QCOMPARE(idx.model(), persistentIndex.model());
+ }
+ }
+ }
+}
+
+void tst_QAbstractItemModel::testMoveToUncle_data()
+{
+
+ QTest::addColumn<int>("startRow");
+ QTest::addColumn<int>("endRow");
+ QTest::addColumn<int>("destRow");
+
+ // Move from the start to the middle
+ QTest::newRow("move01") << 0 << 2 << 8;
+ // Move from the start to the end
+ QTest::newRow("move02") << 0 << 2 << 10;
+ // Move from the middle to the middle
+ QTest::newRow("move03") << 3 << 5 << 8;
+ // Move from the middle to the end
+ QTest::newRow("move04") << 3 << 5 << 10;
+
+ // Move from the middle to the start
+ QTest::newRow("move05") << 5 << 7 << 0;
+ // Move from the end to the start
+ QTest::newRow("move06") << 8 << 9 << 0;
+ // Move from the middle to the middle
+ QTest::newRow("move07") << 5 << 7 << 2;
+ // Move from the end to the middle
+ QTest::newRow("move08") << 8 << 9 << 5;
+
+ // Moving to the same row in a different parent doesn't confuse things.
+ QTest::newRow("move09") << 8 << 8 << 8;
+
+ // Moving to the row of my parent and its neighbours doesn't confuse things
+ QTest::newRow("move09") << 8 << 8 << 4;
+ QTest::newRow("move10") << 8 << 8 << 5;
+ QTest::newRow("move11") << 8 << 8 << 6;
+
+ // Moving everything from one parent to another
+ QTest::newRow("move12") << 0 << 9 << 10;
+}
+
+void tst_QAbstractItemModel::testMoveToUncle()
+{
+ // Need to have some extra rows available.
+ ModelInsertCommand *insertCommand = new ModelInsertCommand(m_model, this);
+ insertCommand->setAncestorRowNumbers(QList<int>() << 9);
+ insertCommand->setNumCols(4);
+ insertCommand->setStartRow(0);
+ insertCommand->setEndRow(9);
+ insertCommand->doCommand();
+
+ QFETCH( int, startRow);
+ QFETCH( int, endRow);
+ QFETCH( int, destRow);
+
+ QList<QPersistentModelIndex> persistentList;
+ QModelIndexList indexList;
+ QModelIndexList parentsList;
+
+ const int column = 0;
+
+ QModelIndex sourceIndex = m_model->index(9, 0);
+ for (int i= 0; i < m_model->rowCount(sourceIndex); ++i)
+ {
+ QModelIndex idx = m_model->index(i, column, sourceIndex);
+ QVERIFY(idx.isValid());
+ indexList << idx;
+ parentsList << idx.parent();
+ persistentList << QPersistentModelIndex(idx);
+ }
+
+ QModelIndex destIndex = m_model->index(5, 0);
+ for (int i= 0; i < m_model->rowCount(destIndex); ++i)
+ {
+ QModelIndex idx = m_model->index(i, column, destIndex);
+ QVERIFY(idx.isValid());
+ indexList << idx;
+ parentsList << idx.parent();
+ persistentList << QPersistentModelIndex(idx);
+ }
+
+ QSignalSpy beforeSpy(m_model, SIGNAL(rowsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int)));
+ QSignalSpy afterSpy(m_model, SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)));
+
+ ModelMoveCommand *moveCommand = new ModelMoveCommand(m_model, this);
+ moveCommand->setAncestorRowNumbers(QList<int>() << 9);
+ moveCommand->setNumCols(4);
+ moveCommand->setStartRow(startRow);
+ moveCommand->setEndRow(endRow);
+ moveCommand->setDestAncestors(QList<int>() << 5);
+ moveCommand->setDestRow(destRow);
+ moveCommand->doCommand();
+
+ QVariantList beforeSignal = beforeSpy.takeAt(0);
+ QVariantList afterSignal = afterSpy.takeAt(0);
+
+ QCOMPARE(beforeSignal.size(), 5);
+ QCOMPARE(beforeSignal.at(0).value<QModelIndex>(), sourceIndex);
+ QCOMPARE(beforeSignal.at(1).toInt(), startRow);
+ QCOMPARE(beforeSignal.at(2).toInt(), endRow);
+ QCOMPARE(beforeSignal.at(3).value<QModelIndex>(), destIndex);
+ QCOMPARE(beforeSignal.at(4).toInt(), destRow);
+
+ QCOMPARE(afterSignal.size(), 5);
+ QCOMPARE(afterSignal.at(0).value<QModelIndex>(), sourceIndex);
+ QCOMPARE(afterSignal.at(1).toInt(), startRow);
+ QCOMPARE(afterSignal.at(2).toInt(), endRow);
+ QCOMPARE(afterSignal.at(3).value<QModelIndex>(), destIndex);
+ QCOMPARE(afterSignal.at(4).toInt(), destRow);
+
+ for (int i = 0; i < indexList.size(); i++)
+ {
+ QModelIndex idx = indexList.at(i);
+ QModelIndex idxParent = parentsList.at(i);
+ QModelIndex persistentIndex = persistentList.at(i);
+
+ int row = idx.row();
+ if (idxParent == destIndex)
+ {
+ if ( row >= destRow)
+ {
+ QCOMPARE(row + endRow - startRow + 1, persistentIndex.row() );
+ QCOMPARE(idx.column(), persistentIndex.column());
+ QCOMPARE(destIndex, persistentIndex.parent());
+ QCOMPARE(idx.model(), persistentIndex.model());
+ } else
+ {
+ QCOMPARE(idx, persistentIndex);
+ }
+ } else
+ {
+ if (row < startRow)
+ {
+ QCOMPARE(idx, persistentIndex);
+ } else if (row <= endRow)
+ {
+ QCOMPARE(row + destRow - startRow, persistentIndex.row() );
+ QCOMPARE(idx.column(), persistentIndex.column());
+ QCOMPARE(destIndex, persistentIndex.parent());
+ QCOMPARE(idx.model(), persistentIndex.model());
+
+ } else {
+ QCOMPARE(row - (endRow - startRow + 1), persistentIndex.row() );
+ QCOMPARE(idx.column(), persistentIndex.column());
+ QCOMPARE(idxParent, persistentIndex.parent());
+ QCOMPARE(idx.model(), persistentIndex.model());
+ }
+ }
+ }
+}
+
+void tst_QAbstractItemModel::testMoveToDescendants()
+{
+ // Attempt to move a row to its ancestors depth rows deep.
+ const int depth = 6;
+
+ // Need to have some extra rows available in a tree.
+ QList<int> rows;
+ ModelInsertCommand *insertCommand;
+ for (int i = 0; i < depth; i++)
+ {
+ insertCommand = new ModelInsertCommand(m_model, this);
+ insertCommand->setAncestorRowNumbers(rows);
+ insertCommand->setNumCols(4);
+ insertCommand->setStartRow(0);
+ insertCommand->setEndRow(9);
+ insertCommand->doCommand();
+ rows << 9;
+ }
+
+ QList<QPersistentModelIndex> persistentList;
+ QModelIndexList indexList;
+ QModelIndexList parentsList;
+
+ const int column = 0;
+
+ QModelIndex sourceIndex = m_model->index(9, 0);
+ for (int i= 0; i < m_model->rowCount(sourceIndex); ++i)
+ {
+ QModelIndex idx = m_model->index(i, column, sourceIndex);
+ QVERIFY(idx.isValid());
+ indexList << idx;
+ parentsList << idx.parent();
+ persistentList << QPersistentModelIndex(idx);
+ }
+
+ QModelIndex destIndex = m_model->index(5, 0);
+ for (int i= 0; i < m_model->rowCount(destIndex); ++i)
+ {
+ QModelIndex idx = m_model->index(i, column, destIndex);
+ QVERIFY(idx.isValid());
+ indexList << idx;
+ parentsList << idx.parent();
+ persistentList << QPersistentModelIndex(idx);
+ }
+
+ QSignalSpy beforeSpy(m_model, SIGNAL(rowsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int)));
+ QSignalSpy afterSpy(m_model, SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)));
+
+ ModelMoveCommand *moveCommand;
+ QList<int> ancestors;
+ while (ancestors.size() < depth)
+ {
+ ancestors << 9;
+ for (int row = 0; row <= 9; row++)
+ {
+ moveCommand = new ModelMoveCommand(m_model, this);
+ moveCommand->setNumCols(4);
+ moveCommand->setStartRow(9);
+ moveCommand->setEndRow(9);
+ moveCommand->setDestAncestors(ancestors);
+ moveCommand->setDestRow(row);
+ moveCommand->doCommand();
+
+ QVERIFY(beforeSpy.size() == 0);
+ QVERIFY(afterSpy.size() == 0);
+ }
+ }
+}
+
+void tst_QAbstractItemModel::testMoveWithinOwnRange_data()
+{
+ QTest::addColumn<int>("startRow");
+ QTest::addColumn<int>("endRow");
+ QTest::addColumn<int>("destRow");
+
+ QTest::newRow("move01") << 0 << 0 << 0;
+ QTest::newRow("move02") << 0 << 0 << 1;
+ QTest::newRow("move03") << 0 << 5 << 0;
+ QTest::newRow("move04") << 0 << 5 << 1;
+ QTest::newRow("move05") << 0 << 5 << 2;
+ QTest::newRow("move06") << 0 << 5 << 3;
+ QTest::newRow("move07") << 0 << 5 << 4;
+ QTest::newRow("move08") << 0 << 5 << 5;
+ QTest::newRow("move09") << 0 << 5 << 6;
+ QTest::newRow("move08") << 3 << 5 << 5;
+ QTest::newRow("move08") << 3 << 5 << 6;
+ QTest::newRow("move09") << 4 << 5 << 5;
+ QTest::newRow("move10") << 4 << 5 << 6;
+ QTest::newRow("move11") << 5 << 5 << 5;
+ QTest::newRow("move12") << 5 << 5 << 6;
+ QTest::newRow("move13") << 5 << 9 << 9;
+ QTest::newRow("move14") << 5 << 9 << 10;
+ QTest::newRow("move15") << 6 << 9 << 9;
+ QTest::newRow("move16") << 6 << 9 << 10;
+ QTest::newRow("move17") << 7 << 9 << 9;
+ QTest::newRow("move18") << 7 << 9 << 10;
+ QTest::newRow("move19") << 8 << 9 << 9;
+ QTest::newRow("move20") << 8 << 9 << 10;
+ QTest::newRow("move21") << 9 << 9 << 9;
+ QTest::newRow("move22") << 0 << 9 << 10;
+
+}
+
+void tst_QAbstractItemModel::testMoveWithinOwnRange()
+{
+
+ QFETCH( int, startRow);
+ QFETCH( int, endRow);
+ QFETCH( int, destRow);
+
+
+ QSignalSpy beforeSpy(m_model, SIGNAL(rowsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int)));
+ QSignalSpy afterSpy(m_model, SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)));
+
+ ModelMoveCommand *moveCommand = new ModelMoveCommand(m_model, this);
+ moveCommand->setNumCols(4);
+ moveCommand->setStartRow(startRow);
+ moveCommand->setEndRow(endRow);
+ moveCommand->setDestRow(destRow);
+ moveCommand->doCommand();
+
+ QVERIFY(beforeSpy.size() == 0);
+ QVERIFY(afterSpy.size() == 0);
+
+
+}
+
+class ListenerObject : public QObject
+{
+ Q_OBJECT
+public:
+ ListenerObject(QAbstractProxyModel *parent);
+
+protected:
+ void fillIndexStores(const QModelIndex &parent);
+
+public slots:
+ void slotAboutToBeReset();
+ void slotReset();
+
+private:
+ QAbstractProxyModel *m_model;
+ QList<QPersistentModelIndex> m_persistentIndexes;
+ QModelIndexList m_nonPersistentIndexes;
+};
+
+
+ListenerObject::ListenerObject(QAbstractProxyModel *parent)
+ : QObject(parent), m_model(parent)
+{
+ connect(m_model, SIGNAL(modelAboutToBeReset()), SLOT(slotAboutToBeReset()));
+ connect(m_model, SIGNAL(modelReset()), SLOT(slotReset()));
+
+ fillIndexStores(QModelIndex());
+}
+
+void ListenerObject::fillIndexStores(const QModelIndex &parent)
+{
+ const int column = 0;
+ int row = 0;
+ QModelIndex idx = m_model->index(row, column, parent);
+ while (idx.isValid())
+ {
+ m_persistentIndexes << QPersistentModelIndex(idx);
+ m_nonPersistentIndexes << idx;
+ if (m_model->hasChildren(idx))
+ {
+ fillIndexStores(idx);
+ }
+ ++row;
+ idx = m_model->index(row, column, parent);
+ }
+}
+
+void ListenerObject::slotAboutToBeReset()
+{
+ // Nothing has been changed yet. All indexes should be the same.
+ for (int i = 0; i < m_persistentIndexes.size(); ++i)
+ {
+ QModelIndex idx = m_persistentIndexes.at(i);
+ QVERIFY(idx == m_nonPersistentIndexes.at(i));
+ QVERIFY(m_model->mapToSource(idx).isValid());
+ }
+}
+
+void ListenerObject::slotReset()
+{
+ foreach(const QModelIndex &idx, m_persistentIndexes)
+ {
+ QVERIFY(!idx.isValid());
+ }
+}
+
+
+void tst_QAbstractItemModel::testReset()
+{
+ QSignalSpy beforeResetSpy(m_model, SIGNAL(modelAboutToBeReset()));
+ QSignalSpy afterResetSpy(m_model, SIGNAL(modelReset()));
+
+
+ QSortFilterProxyModel *nullProxy = new QSortFilterProxyModel(this);
+ nullProxy->setSourceModel(m_model);
+
+ // Makes sure the model and proxy are in a consistent state. before and after reset.
+ new ListenerObject(nullProxy);
+
+ ModelResetCommandFixed *resetCommand = new ModelResetCommandFixed(m_model, this);
+
+ resetCommand->setNumCols(4);
+ resetCommand->setStartRow(0);
+ resetCommand->setEndRow(0);
+ resetCommand->setDestRow(0);
+ resetCommand->setDestAncestors(QList<int>() << 5);
+ resetCommand->doCommand();
+
+ // Verify that the correct signals were emitted
+ QVERIFY(beforeResetSpy.size() == 1);
+ QVERIFY(afterResetSpy.size() == 1);
+
+ // Verify that the move actually happened.
+ QVERIFY(m_model->rowCount() == 9);
+ QModelIndex destIndex = m_model->index(4, 0);
+ QVERIFY(m_model->rowCount(destIndex) == 11);
+
+}
+
+
+QTEST_MAIN(tst_QAbstractItemModel)
+#include "tst_qabstractitemmodel.moc"
diff --git a/tests/auto/corelib/kernel/qcoreapplication/.gitignore b/tests/auto/corelib/kernel/qcoreapplication/.gitignore
new file mode 100644
index 0000000000..8cba8c359f
--- /dev/null
+++ b/tests/auto/corelib/kernel/qcoreapplication/.gitignore
@@ -0,0 +1 @@
+tst_qcoreapplication
diff --git a/tests/auto/corelib/kernel/qcoreapplication/qcoreapplication.pro b/tests/auto/corelib/kernel/qcoreapplication/qcoreapplication.pro
new file mode 100644
index 0000000000..031af39df7
--- /dev/null
+++ b/tests/auto/corelib/kernel/qcoreapplication/qcoreapplication.pro
@@ -0,0 +1,4 @@
+load(qttest_p4)
+SOURCES += tst_qcoreapplication.cpp
+QT = core
+CONFIG += parallel_test
diff --git a/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp b/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp
new file mode 100644
index 0000000000..99f0941ac2
--- /dev/null
+++ b/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp
@@ -0,0 +1,582 @@
+/****************************************************************************
+**
+** 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 <QtCore/QtCore>
+#include <QtTest/QtTest>
+
+class tst_QCoreApplication: public QObject
+{
+ Q_OBJECT
+private slots:
+ void sendEventsOnProcessEvents(); // this must be the first test
+ void getSetCheck();
+ void qAppName();
+ void argc();
+ void postEvent();
+ void removePostedEvents();
+#ifndef QT_NO_THREAD
+ void deliverInDefinedOrder();
+#endif
+ void applicationPid();
+ void globalPostedEventsCount();
+ void processEventsAlwaysSendsPostedEvents();
+ void reexec();
+ void execAfterExit();
+ void eventLoopExecAfterExit();
+};
+
+class EventSpy : public QObject
+{
+ Q_OBJECT
+
+public:
+ QList<int> recordedEvents;
+ bool eventFilter(QObject *, QEvent *event)
+ {
+ recordedEvents.append(event->type());
+ return false;
+ }
+};
+
+void tst_QCoreApplication::sendEventsOnProcessEvents()
+{
+ int argc = 1;
+ char *argv[] = { "tst_qcoreapplication" };
+ QCoreApplication app(argc, argv);
+
+ EventSpy spy;
+ app.installEventFilter(&spy);
+
+ QCoreApplication::postEvent(&app, new QEvent(QEvent::Type(QEvent::User + 1)));
+ QCoreApplication::processEvents();
+ QVERIFY(spy.recordedEvents.contains(QEvent::User + 1));
+}
+
+void tst_QCoreApplication::getSetCheck()
+{
+ // do not crash
+ QString v = QCoreApplication::applicationVersion();
+ v = QLatin1String("3.0.0 prerelease 1");
+ QCoreApplication::setApplicationVersion(v);
+ QCOMPARE(QCoreApplication::applicationVersion(), v);
+
+ // Test the property
+ {
+ int argc = 1;
+ char *argv[] = { "tst_qcoreapplication" };
+ QCoreApplication app(argc, argv);
+ QCOMPARE(app.property("applicationVersion").toString(), v);
+ }
+ v = QString();
+ QCoreApplication::setApplicationVersion(v);
+ QCOMPARE(QCoreApplication::applicationVersion(), v);
+}
+
+void tst_QCoreApplication::qAppName()
+{
+ int argc = 1;
+ char *argv[] = { "tst_qcoreapplication" };
+ QCoreApplication app(argc, argv);
+ QVERIFY(!::qAppName().isEmpty());
+}
+
+void tst_QCoreApplication::argc()
+{
+ {
+ int argc = 1;
+ char *argv[] = { "tst_qcoreapplication" };
+ QCoreApplication app(argc, argv);
+ QCOMPARE(argc, 1);
+ QCOMPARE(app.argc(), 1);
+ }
+
+ {
+ int argc = 4;
+ char *argv[] = { "tst_qcoreapplication", "arg1", "arg2", "arg3" };
+ QCoreApplication app(argc, argv);
+ QCOMPARE(argc, 4);
+ QCOMPARE(app.argc(), 4);
+ }
+
+ {
+ int argc = 0;
+ char **argv = 0;
+ QCoreApplication app(argc, argv);
+ QCOMPARE(argc, 0);
+ QCOMPARE(app.argc(), 0);
+ }
+}
+
+class EventGenerator : public QObject
+{
+ Q_OBJECT
+
+public:
+ QObject *other;
+
+ bool event(QEvent *e)
+ {
+ if (e->type() == QEvent::MaxUser) {
+ QCoreApplication::sendPostedEvents(other, 0);
+ } else if (e->type() <= QEvent::User + 999) {
+ // post a new event in response to this posted event
+ int offset = e->type() - QEvent::User;
+ offset = (offset * 10 + offset % 10);
+ QCoreApplication::postEvent(this, new QEvent(QEvent::Type(QEvent::User + offset)), offset);
+ }
+
+ return QObject::event(e);
+ }
+};
+
+void tst_QCoreApplication::postEvent()
+{
+ int argc = 1;
+ char *argv[] = { "tst_qcoreapplication" };
+ QCoreApplication app(argc, argv);
+
+ EventSpy spy;
+ EventGenerator odd, even;
+ odd.other = &even;
+ odd.installEventFilter(&spy);
+ even.other = &odd;
+ even.installEventFilter(&spy);
+
+ QCoreApplication::postEvent(&odd, new QEvent(QEvent::Type(QEvent::User + 1)));
+ QCoreApplication::postEvent(&even, new QEvent(QEvent::Type(QEvent::User + 2)));
+
+ QCoreApplication::postEvent(&odd, new QEvent(QEvent::Type(QEvent::User + 3)), 1);
+ QCoreApplication::postEvent(&even, new QEvent(QEvent::Type(QEvent::User + 4)), 2);
+
+ QCoreApplication::postEvent(&odd, new QEvent(QEvent::Type(QEvent::User + 5)), -2);
+ QCoreApplication::postEvent(&even, new QEvent(QEvent::Type(QEvent::User + 6)), -1);
+
+ QList<int> expected;
+ expected << QEvent::User + 4
+ << QEvent::User + 3
+ << QEvent::User + 1
+ << QEvent::User + 2
+ << QEvent::User + 6
+ << QEvent::User + 5;
+
+ QCoreApplication::sendPostedEvents();
+ // live lock protection ensures that we only send the initial events
+ QCOMPARE(spy.recordedEvents, expected);
+
+ expected.clear();
+ expected << QEvent::User + 66
+ << QEvent::User + 55
+ << QEvent::User + 44
+ << QEvent::User + 33
+ << QEvent::User + 22
+ << QEvent::User + 11;
+
+ spy.recordedEvents.clear();
+ QCoreApplication::sendPostedEvents();
+ // expect next sequence events
+ QCOMPARE(spy.recordedEvents, expected);
+
+ // have the generators call sendPostedEvents() on each other in
+ // response to an event
+ QCoreApplication::postEvent(&odd, new QEvent(QEvent::MaxUser), INT_MAX);
+ QCoreApplication::postEvent(&even, new QEvent(QEvent::MaxUser), INT_MAX);
+
+ expected.clear();
+ expected << int(QEvent::MaxUser)
+ << int(QEvent::MaxUser)
+ << QEvent::User + 555
+ << QEvent::User + 333
+ << QEvent::User + 111
+ << QEvent::User + 666
+ << QEvent::User + 444
+ << QEvent::User + 222;
+
+ spy.recordedEvents.clear();
+ QCoreApplication::sendPostedEvents();
+ QCOMPARE(spy.recordedEvents, expected);
+
+ expected.clear();
+ expected << QEvent::User + 6666
+ << QEvent::User + 5555
+ << QEvent::User + 4444
+ << QEvent::User + 3333
+ << QEvent::User + 2222
+ << QEvent::User + 1111;
+
+ spy.recordedEvents.clear();
+ QCoreApplication::sendPostedEvents();
+ QCOMPARE(spy.recordedEvents, expected);
+
+ // no more events
+ expected.clear();
+ spy.recordedEvents.clear();
+ QCoreApplication::sendPostedEvents();
+ QCOMPARE(spy.recordedEvents, expected);
+}
+
+void tst_QCoreApplication::removePostedEvents()
+{
+ int argc = 1;
+ char *argv[] = { "tst_qcoreapplication" };
+ QCoreApplication app(argc, argv);
+
+ EventSpy spy;
+ QObject one, two;
+ one.installEventFilter(&spy);
+ two.installEventFilter(&spy);
+
+ QList<int> expected;
+
+ // remove all events for one object
+ QCoreApplication::postEvent(&one, new QEvent(QEvent::Type(QEvent::User + 1)));
+ QCoreApplication::postEvent(&one, new QEvent(QEvent::Type(QEvent::User + 2)));
+ QCoreApplication::postEvent(&one, new QEvent(QEvent::Type(QEvent::User + 3)));
+ QCoreApplication::postEvent(&two, new QEvent(QEvent::Type(QEvent::User + 4)));
+ QCoreApplication::postEvent(&two, new QEvent(QEvent::Type(QEvent::User + 5)));
+ QCoreApplication::postEvent(&two, new QEvent(QEvent::Type(QEvent::User + 6)));
+ QCoreApplication::removePostedEvents(&one);
+ expected << QEvent::User + 4
+ << QEvent::User + 5
+ << QEvent::User + 6;
+ QCoreApplication::sendPostedEvents();
+ QCOMPARE(spy.recordedEvents, expected);
+ spy.recordedEvents.clear();
+ expected.clear();
+
+ // remove all events for all objects
+ QCoreApplication::postEvent(&one, new QEvent(QEvent::Type(QEvent::User + 7)));
+ QCoreApplication::postEvent(&two, new QEvent(QEvent::Type(QEvent::User + 8)));
+ QCoreApplication::postEvent(&one, new QEvent(QEvent::Type(QEvent::User + 9)));
+ QCoreApplication::postEvent(&two, new QEvent(QEvent::Type(QEvent::User + 10)));
+ QCoreApplication::postEvent(&one, new QEvent(QEvent::Type(QEvent::User + 11)));
+ QCoreApplication::postEvent(&two, new QEvent(QEvent::Type(QEvent::User + 12)));
+ QCoreApplication::removePostedEvents(0);
+ QCoreApplication::sendPostedEvents();
+ QVERIFY(spy.recordedEvents.isEmpty());
+
+ // remove a specific type of event for one object
+ QCoreApplication::postEvent(&one, new QEvent(QEvent::Type(QEvent::User + 13)));
+ QCoreApplication::postEvent(&two, new QEvent(QEvent::Type(QEvent::User + 14)));
+ QCoreApplication::postEvent(&one, new QEvent(QEvent::Type(QEvent::User + 15)));
+ QCoreApplication::postEvent(&two, new QEvent(QEvent::Type(QEvent::User + 16)));
+ QCoreApplication::postEvent(&one, new QEvent(QEvent::Type(QEvent::User + 17)));
+ QCoreApplication::postEvent(&two, new QEvent(QEvent::Type(QEvent::User + 18)));
+ QCoreApplication::removePostedEvents(&one, QEvent::User + 13);
+ QCoreApplication::removePostedEvents(&two, QEvent::User + 18);
+ QCoreApplication::sendPostedEvents();
+ expected << QEvent::User + 14
+ << QEvent::User + 15
+ << QEvent::User + 16
+ << QEvent::User + 17;
+ QCOMPARE(spy.recordedEvents, expected);
+ spy.recordedEvents.clear();
+ expected.clear();
+
+ // remove a specific type of event for all objects
+ QCoreApplication::postEvent(&one, new QEvent(QEvent::Type(QEvent::User + 19)));
+ QCoreApplication::postEvent(&two, new QEvent(QEvent::Type(QEvent::User + 19)));
+ QCoreApplication::postEvent(&one, new QEvent(QEvent::Type(QEvent::User + 20)));
+ QCoreApplication::postEvent(&two, new QEvent(QEvent::Type(QEvent::User + 20)));
+ QCoreApplication::postEvent(&one, new QEvent(QEvent::Type(QEvent::User + 21)));
+ QCoreApplication::postEvent(&two, new QEvent(QEvent::Type(QEvent::User + 21)));
+ QCoreApplication::removePostedEvents(0, QEvent::User + 20);
+ QCoreApplication::sendPostedEvents();
+ expected << QEvent::User + 19
+ << QEvent::User + 19
+ << QEvent::User + 21
+ << QEvent::User + 21;
+ QCOMPARE(spy.recordedEvents, expected);
+ spy.recordedEvents.clear();
+ expected.clear();
+}
+
+#ifndef QT_NO_THREAD
+class DeliverInDefinedOrderThread : public QThread
+{
+ Q_OBJECT
+
+public:
+ DeliverInDefinedOrderThread()
+ : QThread()
+ { }
+
+signals:
+ void progress(int);
+
+protected:
+ void run()
+ {
+ emit progress(1);
+ emit progress(2);
+ emit progress(3);
+ emit progress(4);
+ emit progress(5);
+ emit progress(6);
+ emit progress(7);
+ }
+};
+
+class DeliverInDefinedOrderObject : public QObject
+{
+ Q_OBJECT
+
+ QPointer<QThread> thread;
+ int count;
+ int startCount;
+ int loopLevel;
+
+public:
+ DeliverInDefinedOrderObject(QObject *parent)
+ : QObject(parent), thread(0), count(0), startCount(0), loopLevel(0)
+ { }
+
+signals:
+ void done();
+
+public slots:
+ void startThread()
+ {
+ QVERIFY(!thread);
+ thread = new DeliverInDefinedOrderThread();
+ connect(thread, SIGNAL(progress(int)), this, SLOT(threadProgress(int)));
+ connect(thread, SIGNAL(finished()), this, SLOT(threadFinished()));
+ connect(thread, SIGNAL(destroyed()), this, SLOT(threadDestroyed()));
+ thread->start();
+
+ QCoreApplication::postEvent(this, new QEvent(QEvent::MaxUser), -1);
+ }
+
+ void threadProgress(int v)
+ {
+ ++count;
+ QVERIFY(v == count);
+
+ QCoreApplication::postEvent(this, new QEvent(QEvent::MaxUser), -1);
+ }
+
+ void threadFinished()
+ {
+ QVERIFY(count == 7);
+ count = 0;
+ thread->deleteLater();
+
+ QCoreApplication::postEvent(this, new QEvent(QEvent::MaxUser), -1);
+ }
+
+ void threadDestroyed()
+ {
+ if (++startCount < 20)
+ startThread();
+ else
+ emit done();
+ }
+
+public:
+ bool event(QEvent *event)
+ {
+ switch (event->type()) {
+ case QEvent::User:
+ {
+ ++loopLevel;
+ if (loopLevel == 2) {
+ // Ready. Starts a thread that emits (queued) signals, which should be handled in order
+ startThread();
+ }
+ QCoreApplication::postEvent(this, new QEvent(QEvent::MaxUser), -1);
+ (void) QEventLoop().exec();
+ break;
+ }
+ default:
+ break;
+ }
+ return QObject::event(event);
+ }
+};
+
+void tst_QCoreApplication::deliverInDefinedOrder()
+{
+ int argc = 1;
+ char *argv[] = { "tst_qcoreapplication" };
+ QCoreApplication app(argc, argv);
+
+ DeliverInDefinedOrderObject obj(&app);
+ // causes sendPostedEvents() to recurse twice
+ QCoreApplication::postEvent(&obj, new QEvent(QEvent::User));
+ QCoreApplication::postEvent(&obj, new QEvent(QEvent::User));
+
+ QObject::connect(&obj, SIGNAL(done()), &app, SLOT(quit()));
+ app.exec();
+}
+#endif // QT_NO_QTHREAD
+
+void tst_QCoreApplication::applicationPid()
+{
+ QVERIFY(QCoreApplication::applicationPid() > 0);
+}
+
+QT_BEGIN_NAMESPACE
+Q_CORE_EXPORT uint qGlobalPostedEventsCount();
+QT_END_NAMESPACE
+
+class GlobalPostedEventsCountObject : public QObject
+{
+ Q_OBJECT
+
+public:
+ QList<int> globalPostedEventsCount;
+
+ bool event(QEvent *event)
+ {
+ if (event->type() == QEvent::User)
+ globalPostedEventsCount.append(qGlobalPostedEventsCount());
+ return QObject::event(event);
+ }
+};
+
+void tst_QCoreApplication::globalPostedEventsCount()
+{
+ int argc = 1;
+ char *argv[] = { "tst_qcoreapplication" };
+ QCoreApplication app(argc, argv);
+
+ QCoreApplication::sendPostedEvents();
+ QCOMPARE(qGlobalPostedEventsCount(), 0u);
+
+ GlobalPostedEventsCountObject x;
+ QCoreApplication::postEvent(&x, new QEvent(QEvent::User));
+ QCoreApplication::postEvent(&x, new QEvent(QEvent::User));
+ QCoreApplication::postEvent(&x, new QEvent(QEvent::User));
+ QCoreApplication::postEvent(&x, new QEvent(QEvent::User));
+ QCoreApplication::postEvent(&x, new QEvent(QEvent::User));
+ QCOMPARE(qGlobalPostedEventsCount(), 5u);
+
+ QCoreApplication::sendPostedEvents();
+ QCOMPARE(qGlobalPostedEventsCount(), 0u);
+
+ QList<int> expected = QList<int>()
+ << 4
+ << 3
+ << 2
+ << 1
+ << 0;
+ QCOMPARE(x.globalPostedEventsCount, expected);
+}
+
+class ProcessEventsAlwaysSendsPostedEventsObject : public QObject
+{
+public:
+ int counter;
+
+ inline ProcessEventsAlwaysSendsPostedEventsObject()
+ : counter(0)
+ { }
+
+ bool event(QEvent *event)
+ {
+ if (event->type() == QEvent::User)
+ ++counter;
+ return QObject::event(event);
+ }
+};
+
+void tst_QCoreApplication::processEventsAlwaysSendsPostedEvents()
+{
+ int argc = 1;
+ char *argv[] = { "tst_qcoreapplication" };
+ QCoreApplication app(argc, argv);
+
+ ProcessEventsAlwaysSendsPostedEventsObject object;
+ QTime t;
+ t.start();
+ int i = 1;
+ do {
+ QCoreApplication::postEvent(&object, new QEvent(QEvent::User));
+ QCoreApplication::processEvents();
+ QCOMPARE(object.counter, i);
+ ++i;
+ } while (t.elapsed() < 1000);
+}
+
+void tst_QCoreApplication::reexec()
+{
+ int argc = 1;
+ char *argv[] = { "tst_qcoreapplication" };
+ QCoreApplication app(argc, argv);
+
+ // exec once
+ QMetaObject::invokeMethod(&app, "quit", Qt::QueuedConnection);
+ QCOMPARE(app.exec(), 0);
+
+ // and again
+ QMetaObject::invokeMethod(&app, "quit", Qt::QueuedConnection);
+ QCOMPARE(app.exec(), 0);
+}
+
+void tst_QCoreApplication::execAfterExit()
+{
+ int argc = 1;
+ char *argv[] = { "tst_qcoreapplication" };
+ QCoreApplication app(argc, argv);
+
+ app.exit(1);
+ QMetaObject::invokeMethod(&app, "quit", Qt::QueuedConnection);
+ QCOMPARE(app.exec(), 0);
+}
+
+void tst_QCoreApplication::eventLoopExecAfterExit()
+{
+ int argc = 1;
+ char *argv[] = { "tst_qcoreapplication" };
+ QCoreApplication app(argc, argv);
+
+ // exec once and exit
+ QMetaObject::invokeMethod(&app, "quit", Qt::QueuedConnection);
+ QCOMPARE(app.exec(), 0);
+
+ // and again, but this time using a QEventLoop
+ QEventLoop loop;
+ QMetaObject::invokeMethod(&loop, "quit", Qt::QueuedConnection);
+ QCOMPARE(loop.exec(), 0);
+}
+
+QTEST_APPLESS_MAIN(tst_QCoreApplication)
+#include "tst_qcoreapplication.moc"
diff --git a/tests/auto/corelib/kernel/qeventloop/.gitignore b/tests/auto/corelib/kernel/qeventloop/.gitignore
new file mode 100644
index 0000000000..be3297188a
--- /dev/null
+++ b/tests/auto/corelib/kernel/qeventloop/.gitignore
@@ -0,0 +1 @@
+tst_qeventloop
diff --git a/tests/auto/corelib/kernel/qeventloop/qeventloop.pro b/tests/auto/corelib/kernel/qeventloop/qeventloop.pro
new file mode 100644
index 0000000000..e3c170a33b
--- /dev/null
+++ b/tests/auto/corelib/kernel/qeventloop/qeventloop.pro
@@ -0,0 +1,8 @@
+load(qttest_p4)
+SOURCES += tst_qeventloop.cpp
+QT -= gui
+QT += network
+
+win32:!wince*:LIBS += -luser32
+
+symbian:TARGET.CAPABILITY += NetworkServices
diff --git a/tests/auto/corelib/kernel/qeventloop/tst_qeventloop.cpp b/tests/auto/corelib/kernel/qeventloop/tst_qeventloop.cpp
new file mode 100644
index 0000000000..6f8a75ed3d
--- /dev/null
+++ b/tests/auto/corelib/kernel/qeventloop/tst_qeventloop.cpp
@@ -0,0 +1,937 @@
+/****************************************************************************
+**
+** 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 <qabstracteventdispatcher.h>
+#include <qcoreapplication.h>
+#include <qcoreevent.h>
+#include <qeventloop.h>
+#include <qmutex.h>
+#include <qthread.h>
+#include <qtimer.h>
+#include <qwaitcondition.h>
+#include <QTcpServer>
+#include <QTcpSocket>
+
+#ifdef Q_OS_SYMBIAN
+#include <e32base.h>
+#include <unistd.h>
+#endif
+
+#include "../../../../shared/util.h"
+
+//TESTED_CLASS=
+//TESTED_FILES=
+
+class EventLoopExiter : public QObject
+{
+ Q_OBJECT
+ QEventLoop *eventLoop;
+public:
+ inline EventLoopExiter(QEventLoop *el)
+ : eventLoop(el)
+ { }
+public slots:
+ void exit();
+ void exit1();
+ void exit2();
+};
+
+void EventLoopExiter::exit()
+{ eventLoop->exit(); }
+
+void EventLoopExiter::exit1()
+{ eventLoop->exit(1); }
+
+void EventLoopExiter::exit2()
+{ eventLoop->exit(2); }
+
+class EventLoopThread : public QThread
+{
+ Q_OBJECT
+signals:
+ void checkPoint();
+public:
+ QEventLoop *eventLoop;
+ void run();
+};
+
+void EventLoopThread::run()
+{
+ eventLoop = new QEventLoop;
+ emit checkPoint();
+ (void) eventLoop->exec();
+ delete eventLoop;
+ eventLoop = 0;
+}
+
+class MultipleExecThread : public QThread
+{
+ Q_OBJECT
+signals:
+ void checkPoint();
+public:
+ QMutex mutex;
+ QWaitCondition cond;
+ volatile int result1;
+ volatile int result2;
+ MultipleExecThread() : result1(0xdead), result2(0xbeef) {}
+
+ void run()
+ {
+ QMutexLocker locker(&mutex);
+ // this exec should work
+
+ cond.wakeOne();
+ cond.wait(&mutex);
+
+ QTimer timer;
+ connect(&timer, SIGNAL(timeout()), SLOT(quit()), Qt::DirectConnection);
+ timer.setInterval(1000);
+ timer.start();
+ result1 = exec();
+
+ // this should return immediately, since exit() has been called
+ cond.wakeOne();
+ cond.wait(&mutex);
+ QEventLoop eventLoop;
+ result2 = eventLoop.exec();
+ }
+};
+
+class StartStopEvent: public QEvent
+{
+public:
+ StartStopEvent(int type, QEventLoop *loop = 0)
+ : QEvent(Type(type)), el(loop)
+ { }
+
+ QEventLoop *el;
+};
+
+class EventLoopExecutor : public QObject
+{
+ Q_OBJECT
+ QEventLoop *eventLoop;
+public:
+ int returnCode;
+ EventLoopExecutor(QEventLoop *eventLoop)
+ : QObject(), eventLoop(eventLoop), returnCode(-42)
+ {
+ }
+public slots:
+ void exec()
+ {
+ QTimer::singleShot(100, eventLoop, SLOT(quit()));
+ // this should return immediately, and the timer event should be delivered to
+ // tst_QEventLoop::exec() test, letting the test complete
+ returnCode = eventLoop->exec();
+ }
+};
+
+#ifndef QT_NO_EXCEPTIONS
+class QEventLoopTestException { };
+
+class ExceptionThrower : public QObject
+{
+ Q_OBJECT
+public:
+ ExceptionThrower() : QObject() { }
+public slots:
+ void throwException()
+ {
+ QEventLoopTestException e;
+ throw e;
+ }
+};
+#endif
+
+class tst_QEventLoop : public QObject
+{
+ Q_OBJECT
+public:
+ tst_QEventLoop();
+ ~tst_QEventLoop();
+public slots:
+ void init();
+ void cleanup();
+private slots:
+ // This test *must* run first. See the definition for why.
+ void onlySymbianActiveScheduler();
+ void symbianNestedActiveSchedulerLoop_data();
+ void symbianNestedActiveSchedulerLoop();
+ void processEvents();
+ void exec();
+ void throwInExec();
+ void reexec();
+ void exit();
+ void execAfterExit();
+ void wakeUp();
+ void quit();
+ void processEventsExcludeSocket();
+ void processEventsExcludeTimers();
+ void deliverInDefinedOrder_QTBUG19637();
+
+ // keep this test last:
+ void nestedLoops();
+
+protected:
+ void customEvent(QEvent *e);
+};
+
+tst_QEventLoop::tst_QEventLoop()
+{ }
+
+tst_QEventLoop::~tst_QEventLoop()
+{ }
+
+void tst_QEventLoop::init()
+{ }
+
+void tst_QEventLoop::cleanup()
+{ }
+
+#ifdef Q_OS_SYMBIAN
+class OnlySymbianActiveScheduler_helper : public QObject
+{
+ Q_OBJECT
+
+public:
+ OnlySymbianActiveScheduler_helper(int fd, QTimer *zeroTimer)
+ : fd(fd),
+ timerCount(0),
+ zeroTimer(zeroTimer),
+ zeroTimerCount(0),
+ notifierCount(0)
+ {
+ }
+ ~OnlySymbianActiveScheduler_helper() {}
+
+public slots:
+ void timerSlot()
+ {
+ // Let all the events occur twice so we know they reactivated after
+ // each occurrence.
+ if (++timerCount >= 2) {
+ // This will hopefully run last, so stop the active scheduler.
+ CActiveScheduler::Stop();
+ }
+ }
+ void zeroTimerSlot()
+ {
+ if (++zeroTimerCount >= 2) {
+ zeroTimer->stop();
+ }
+ }
+ void notifierSlot()
+ {
+ if (++notifierCount >= 2) {
+ char dummy;
+ ::read(fd, &dummy, 1);
+ }
+ }
+
+private:
+ int fd;
+ int timerCount;
+ QTimer *zeroTimer;
+ int zeroTimerCount;
+ int notifierCount;
+};
+#endif
+
+void tst_QEventLoop::onlySymbianActiveScheduler() {
+#ifndef Q_OS_SYMBIAN
+ QSKIP("This is a Symbian-only test.", SkipAll);
+#else
+ // In here we try to use timers and sockets exclusively using the Symbian
+ // active scheduler and no processEvents().
+ // This test should therefore be run first, so that we can verify that
+ // the first occurrence of processEvents does not do any initialization that
+ // we depend on.
+
+ // Open up a pipe so we can test socket notifiers.
+ int pipeEnds[2];
+ if (::pipe(pipeEnds) != 0) {
+ QFAIL("Could not open pipe");
+ }
+ QSocketNotifier notifier(pipeEnds[0], QSocketNotifier::Read);
+ QSignalSpy notifierSpy(&notifier, SIGNAL(activated(int)));
+ char dummy = 1;
+ ::write(pipeEnds[1], &dummy, 1);
+
+ QTimer zeroTimer;
+ QSignalSpy zeroTimerSpy(&zeroTimer, SIGNAL(timeout()));
+ zeroTimer.setInterval(0);
+ zeroTimer.start();
+
+ QTimer timer;
+ QSignalSpy timerSpy(&timer, SIGNAL(timeout()));
+ timer.setInterval(2000); // Generous timeout or this test will fail if there is high load
+ timer.start();
+
+ OnlySymbianActiveScheduler_helper helper(pipeEnds[0], &zeroTimer);
+ connect(&notifier, SIGNAL(activated(int)), &helper, SLOT(notifierSlot()));
+ connect(&zeroTimer, SIGNAL(timeout()), &helper, SLOT(zeroTimerSlot()));
+ connect(&timer, SIGNAL(timeout()), &helper, SLOT(timerSlot()));
+
+ CActiveScheduler::Start();
+
+ ::close(pipeEnds[1]);
+ ::close(pipeEnds[0]);
+
+ QCOMPARE(notifierSpy.count(), 2);
+ QCOMPARE(zeroTimerSpy.count(), 2);
+ QCOMPARE(timerSpy.count(), 2);
+#endif
+}
+
+void tst_QEventLoop::processEvents()
+{
+ QSignalSpy spy1(QAbstractEventDispatcher::instance(), SIGNAL(aboutToBlock()));
+ QSignalSpy spy2(QAbstractEventDispatcher::instance(), SIGNAL(awake()));
+
+ QEventLoop eventLoop;
+
+ QCoreApplication::postEvent(&eventLoop, new QEvent(QEvent::User));
+
+ // process posted events, QEventLoop::processEvents() should return
+ // true
+ QVERIFY(eventLoop.processEvents());
+ QCOMPARE(spy1.count(), 0);
+ QCOMPARE(spy2.count(), 1);
+
+ // allow any session manager to complete its handshake, so that
+ // there are no pending events left.
+ while (eventLoop.processEvents())
+ ;
+
+ // On mac we get application started events at this point,
+ // so process events one more time just to be sure.
+ eventLoop.processEvents();
+
+ // no events to process, QEventLoop::processEvents() should return
+ // false
+ spy1.clear();
+ spy2.clear();
+ QVERIFY(!eventLoop.processEvents());
+ QCOMPARE(spy1.count(), 0);
+ QCOMPARE(spy2.count(), 1);
+
+ // make sure the test doesn't block forever
+ int timerId = startTimer(100);
+
+ // wait for more events to process, QEventLoop::processEvents()
+ // should return true
+ spy1.clear();
+ spy2.clear();
+ QVERIFY(eventLoop.processEvents(QEventLoop::WaitForMoreEvents));
+
+ // Verify that the eventloop has blocked and woken up. Some eventloops
+ // may block and wake up multiple times.
+ QVERIFY(spy1.count() > 0);
+ QVERIFY(spy2.count() > 0);
+ // We should get one awake for each aboutToBlock, plus one awake when
+ // processEvents is entered.
+ QVERIFY(spy2.count() >= spy1.count());
+
+ killTimer(timerId);
+}
+
+#if defined(Q_OS_SYMBIAN) && defined(Q_CC_NOKIAX86)
+// Symbian needs bit longer timeout for emulator, as emulator startup causes additional delay
+# define EXEC_TIMEOUT 1000
+#else
+# define EXEC_TIMEOUT 100
+#endif
+
+
+void tst_QEventLoop::exec()
+{
+ {
+ QEventLoop eventLoop;
+ EventLoopExiter exiter(&eventLoop);
+ int returnCode;
+
+ QTimer::singleShot(EXEC_TIMEOUT, &exiter, SLOT(exit()));
+ returnCode = eventLoop.exec();
+ QCOMPARE(returnCode, 0);
+
+ QTimer::singleShot(EXEC_TIMEOUT, &exiter, SLOT(exit1()));
+ returnCode = eventLoop.exec();
+ QCOMPARE(returnCode, 1);
+
+ QTimer::singleShot(EXEC_TIMEOUT, &exiter, SLOT(exit2()));
+ returnCode = eventLoop.exec();
+ QCOMPARE(returnCode, 2);
+ }
+
+ {
+ // calling QEventLoop::exec() after a thread loop has exit()ed should return immediately
+ // Note: this behaviour differs from QCoreApplication and QEventLoop
+ // see tst_QCoreApplication::eventLoopExecAfterExit, tst_QEventLoop::reexec
+ MultipleExecThread thread;
+
+ // start thread and wait for checkpoint
+ thread.mutex.lock();
+ thread.start();
+ thread.cond.wait(&thread.mutex);
+
+ // make sure the eventloop runs
+ QSignalSpy spy(QAbstractEventDispatcher::instance(&thread), SIGNAL(awake()));
+ thread.cond.wakeOne();
+ thread.cond.wait(&thread.mutex);
+ QVERIFY(spy.count() > 0);
+ int v = thread.result1;
+ QCOMPARE(v, 0);
+
+ // exec should return immediately
+ spy.clear();
+ thread.cond.wakeOne();
+ thread.mutex.unlock();
+ thread.wait();
+ QCOMPARE(spy.count(), 0);
+ v = thread.result2;
+ QCOMPARE(v, -1);
+ }
+
+ {
+ // a single instance of QEventLoop should not be allowed to recurse into exec()
+ QEventLoop eventLoop;
+ EventLoopExecutor executor(&eventLoop);
+
+ QTimer::singleShot(EXEC_TIMEOUT, &executor, SLOT(exec()));
+ int returnCode = eventLoop.exec();
+ QCOMPARE(returnCode, 0);
+ QCOMPARE(executor.returnCode, -1);
+ }
+}
+
+void tst_QEventLoop::throwInExec()
+{
+#if defined(QT_NO_EXCEPTIONS) || defined(NO_EVENTLOOP_EXCEPTIONS)
+ QSKIP("Exceptions are disabled", SkipAll);
+#elif defined(Q_OS_WINCE_WM) || defined(Q_OS_SYMBIAN)
+ // Windows Mobile cannot handle cross library exceptions
+ // qobject.cpp will try to rethrow the exception after handling
+ // which causes gwes.exe to crash
+
+ // Symbian doesn't propagate exceptions from eventloop, but converts them to
+ // CActiveScheduler errors instead -> this test will hang.
+ QSKIP("This platform doesn't support propagating exceptions through the event loop", SkipAll);
+#elif defined(Q_OS_LINUX)
+ // C++ exceptions can't be passed through glib callbacks. Skip the test if
+ // we're using the glib event loop.
+ QByteArray dispatcher = QAbstractEventDispatcher::instance()->metaObject()->className();
+ if (dispatcher.contains("Glib")) {
+ QSKIP(
+ qPrintable(QString(
+ "Throwing exceptions in exec() won't work if %1 event dispatcher is used.\n"
+ "Try running with QT_NO_GLIB=1 in environment."
+ ).arg(QString::fromLatin1(dispatcher))),
+ SkipAll
+ );
+ }
+#endif
+
+ {
+ // QEventLoop::exec() is exception safe
+ QEventLoop eventLoop;
+ int caughtExceptions = 0;
+
+ try {
+ ExceptionThrower exceptionThrower;
+ QTimer::singleShot(EXEC_TIMEOUT, &exceptionThrower, SLOT(throwException()));
+ (void) eventLoop.exec();
+ } catch (...) {
+ ++caughtExceptions;
+ }
+ try {
+ ExceptionThrower exceptionThrower;
+ QTimer::singleShot(EXEC_TIMEOUT, &exceptionThrower, SLOT(throwException()));
+ (void) eventLoop.exec();
+ } catch (...) {
+ ++caughtExceptions;
+ }
+ QCOMPARE(caughtExceptions, 2);
+ }
+}
+
+void tst_QEventLoop::reexec()
+{
+ QEventLoop loop;
+
+ // exec once
+ QMetaObject::invokeMethod(&loop, "quit", Qt::QueuedConnection);
+ QCOMPARE(loop.exec(), 0);
+
+ // and again
+ QMetaObject::invokeMethod(&loop, "quit", Qt::QueuedConnection);
+ QCOMPARE(loop.exec(), 0);
+}
+
+void tst_QEventLoop::exit()
+{ DEPENDS_ON(exec()); }
+
+void tst_QEventLoop::execAfterExit()
+{
+ QEventLoop loop;
+ EventLoopExiter obj(&loop);
+
+ QMetaObject::invokeMethod(&obj, "exit", Qt::QueuedConnection);
+ loop.exit(1);
+ QCOMPARE(loop.exec(), 0);
+}
+
+void tst_QEventLoop::wakeUp()
+{
+ EventLoopThread thread;
+ QEventLoop eventLoop;
+ connect(&thread, SIGNAL(checkPoint()), &eventLoop, SLOT(quit()));
+ connect(&thread, SIGNAL(finished()), &eventLoop, SLOT(quit()));
+
+ thread.start();
+ (void) eventLoop.exec();
+
+ QSignalSpy spy(QAbstractEventDispatcher::instance(&thread), SIGNAL(awake()));
+ thread.eventLoop->wakeUp();
+
+ // give the thread time to wake up
+ QTimer::singleShot(1000, &eventLoop, SLOT(quit()));
+ (void) eventLoop.exec();
+
+ QVERIFY(spy.count() > 0);
+
+ thread.quit();
+ (void) eventLoop.exec();
+}
+
+void tst_QEventLoop::quit()
+{
+ QEventLoop eventLoop;
+ int returnCode;
+
+ QTimer::singleShot(100, &eventLoop, SLOT(quit()));
+ returnCode = eventLoop.exec();
+ QCOMPARE(returnCode, 0);
+}
+
+
+void tst_QEventLoop::nestedLoops()
+{
+ QCoreApplication::postEvent(this, new StartStopEvent(QEvent::User));
+ QCoreApplication::postEvent(this, new StartStopEvent(QEvent::User));
+ QCoreApplication::postEvent(this, new StartStopEvent(QEvent::User));
+
+ // without the fix, this will *wedge* and never return
+ QTest::qWait(1000);
+}
+
+void tst_QEventLoop::customEvent(QEvent *e)
+{
+ if (e->type() == QEvent::User) {
+ QEventLoop loop;
+ QCoreApplication::postEvent(this, new StartStopEvent(int(QEvent::User) + 1, &loop));
+ loop.exec();
+ } else {
+ static_cast<StartStopEvent *>(e)->el->exit();
+ }
+}
+
+class SocketEventsTester: public QObject
+{
+ Q_OBJECT
+public:
+ SocketEventsTester()
+ {
+ socket = 0;
+ server = 0;
+ dataArrived = false;
+ testResult = false;
+ }
+ ~SocketEventsTester()
+ {
+ delete socket;
+ delete server;
+ }
+ bool init()
+ {
+ bool ret = false;
+ server = new QTcpServer();
+ socket = new QTcpSocket();
+ connect(server, SIGNAL(newConnection()), this, SLOT(sendHello()));
+ connect(socket, SIGNAL(readyRead()), this, SLOT(sendAck()), Qt::DirectConnection);
+ if((ret = server->listen(QHostAddress::LocalHost, 0))) {
+ socket->connectToHost(server->serverAddress(), server->serverPort());
+ socket->waitForConnected();
+ }
+ return ret;
+ }
+
+ QTcpSocket *socket;
+ QTcpServer *server;
+ bool dataArrived;
+ bool testResult;
+public slots:
+ void sendAck()
+ {
+ dataArrived = true;
+ }
+ void sendHello()
+ {
+ char data[10] ="HELLO";
+ qint64 size = sizeof(data);
+
+ QTcpSocket *serverSocket = server->nextPendingConnection();
+ serverSocket->write(data, size);
+ serverSocket->flush();
+ QTest::qSleep(200); //allow the TCP/IP stack time to loopback the data, so our socket is ready to read
+ QCoreApplication::processEvents(QEventLoop::ExcludeSocketNotifiers);
+ testResult = dataArrived;
+ QCoreApplication::processEvents(); //check the deferred event is processed
+ serverSocket->close();
+ QThread::currentThread()->exit(0);
+ }
+};
+
+class SocketTestThread : public QThread
+{
+ Q_OBJECT
+public:
+ SocketTestThread():QThread(0),testResult(false){};
+ void run()
+ {
+ SocketEventsTester *tester = new SocketEventsTester();
+ if (tester->init())
+ exec();
+ testResult = tester->testResult;
+ dataArrived = tester->dataArrived;
+ delete tester;
+ }
+ bool testResult;
+ bool dataArrived;
+};
+
+void tst_QEventLoop::processEventsExcludeSocket()
+{
+ SocketTestThread thread;
+ thread.start();
+ QVERIFY(thread.wait());
+ QVERIFY(!thread.testResult);
+ QVERIFY(thread.dataArrived);
+}
+
+class TimerReceiver : public QObject
+{
+public:
+ int gotTimerEvent;
+
+ TimerReceiver()
+ : QObject(), gotTimerEvent(-1)
+ { }
+
+ void timerEvent(QTimerEvent *event)
+ {
+ gotTimerEvent = event->timerId();
+ }
+};
+
+void tst_QEventLoop::processEventsExcludeTimers()
+{
+ TimerReceiver timerReceiver;
+ int timerId = timerReceiver.startTimer(0);
+
+ QEventLoop eventLoop;
+
+ // normal process events will send timers
+ eventLoop.processEvents();
+ QCOMPARE(timerReceiver.gotTimerEvent, timerId);
+ timerReceiver.gotTimerEvent = -1;
+
+ // normal process events will send timers
+ eventLoop.processEvents(QEventLoop::X11ExcludeTimers);
+#if !defined(Q_OS_UNIX) || defined(Q_OS_SYMBIAN)
+ QEXPECT_FAIL("", "X11ExcludeTimers only works on UN*X", Continue);
+#endif
+ QCOMPARE(timerReceiver.gotTimerEvent, -1);
+ timerReceiver.gotTimerEvent = -1;
+
+ // resume timer processing
+ eventLoop.processEvents();
+ QCOMPARE(timerReceiver.gotTimerEvent, timerId);
+ timerReceiver.gotTimerEvent = -1;
+}
+
+#ifdef Q_OS_SYMBIAN
+class DummyActiveObject : public CActive
+{
+public:
+ DummyActiveObject(int levels);
+ ~DummyActiveObject();
+
+ void Start();
+
+protected:
+ void DoCancel();
+ void RunL();
+
+public:
+ bool succeeded;
+
+private:
+ RTimer m_rTimer;
+ int remainingLevels;
+};
+
+class ActiveSchedulerLoop : public QObject
+{
+public:
+ ActiveSchedulerLoop(int levels) : succeeded(false), timerId(-1), remainingLevels(levels) {}
+ ~ActiveSchedulerLoop() {}
+
+ void timerEvent(QTimerEvent *e);
+
+public:
+ bool succeeded;
+ int timerId;
+ int remainingLevels;
+};
+
+DummyActiveObject::DummyActiveObject(int levels)
+ : CActive(CActive::EPriorityStandard),
+ succeeded(false),
+ remainingLevels(levels)
+{
+ m_rTimer.CreateLocal();
+}
+
+DummyActiveObject::~DummyActiveObject()
+{
+ Cancel();
+ m_rTimer.Close();
+}
+
+void DummyActiveObject::DoCancel()
+{
+ m_rTimer.Cancel();
+}
+
+void DummyActiveObject::RunL()
+{
+ if (remainingLevels - 1 <= 0) {
+ ActiveSchedulerLoop loop(remainingLevels - 1);
+ loop.timerId = loop.startTimer(0);
+ QCoreApplication::processEvents();
+
+ succeeded = loop.succeeded;
+ } else {
+ succeeded = true;
+ }
+ CActiveScheduler::Stop();
+}
+
+void DummyActiveObject::Start()
+{
+ m_rTimer.After(iStatus, 100000); // 100 ms
+ SetActive();
+}
+
+void ActiveSchedulerLoop::timerEvent(QTimerEvent *e)
+{
+ Q_UNUSED(e);
+ DummyActiveObject *dummy = new(ELeave) DummyActiveObject(remainingLevels);
+ CActiveScheduler::Add(dummy);
+
+ dummy->Start();
+
+ CActiveScheduler::Start();
+
+ succeeded = dummy->succeeded;
+
+ delete dummy;
+
+ killTimer(timerId);
+}
+
+// We cannot trap panics when the test case fails, so run it in a different thread instead.
+class ActiveSchedulerThread : public QThread
+{
+public:
+ ActiveSchedulerThread(QEventLoop::ProcessEventsFlag flags);
+ ~ActiveSchedulerThread();
+
+protected:
+ void run();
+
+public:
+ volatile bool succeeded;
+
+private:
+ QEventLoop::ProcessEventsFlag m_flags;
+};
+
+ActiveSchedulerThread::ActiveSchedulerThread(QEventLoop::ProcessEventsFlag flags)
+ : succeeded(false),
+ m_flags(flags)
+{
+}
+
+ActiveSchedulerThread::~ActiveSchedulerThread()
+{
+}
+
+void ActiveSchedulerThread::run()
+{
+ ActiveSchedulerLoop loop(2);
+ loop.timerId = loop.startTimer(0);
+ // It may panic in here if the active scheduler and the Qt loop don't go together.
+ QCoreApplication::processEvents(m_flags);
+
+ succeeded = loop.succeeded;
+}
+#endif // ifdef Q_OS_SYMBIAN
+
+void tst_QEventLoop::symbianNestedActiveSchedulerLoop_data()
+{
+ QTest::addColumn<int>("processEventFlags");
+
+ QTest::newRow("AllEvents") << (int)QEventLoop::AllEvents;
+ QTest::newRow("WaitForMoreEvents") << (int)QEventLoop::WaitForMoreEvents;
+}
+
+/*
+ Before you start fiddling with this test, you should have a good understanding of how
+ Symbian active objects work. What the test does is to try to screw up the semaphore count
+ in the active scheduler to cause stray signals, by running the Qt event loop and the
+ active scheduler inside each other. Naturally, its attempts to do this should be futile!
+*/
+void tst_QEventLoop::symbianNestedActiveSchedulerLoop()
+{
+#ifndef Q_OS_SYMBIAN
+ QSKIP("This is a Symbian only test.", SkipAll);
+#else
+ QFETCH(int, processEventFlags);
+
+ ActiveSchedulerThread thread((QEventLoop::ProcessEventsFlag)processEventFlags);
+ thread.start();
+ thread.wait(2000);
+
+ QVERIFY(thread.succeeded);
+#endif
+}
+
+Q_DECLARE_METATYPE(QThread*)
+
+namespace DeliverInDefinedOrder_QTBUG19637 {
+ enum { NbThread = 3, NbObject = 500, NbEventQueue = 5, NbEvent = 50 };
+
+ struct CustomEvent : public QEvent {
+ CustomEvent(int q, int v) : QEvent(Type(User + q)), value(v) {}
+ int value;
+ };
+
+ struct Object : public QObject {
+ Q_OBJECT
+ public:
+ Object() : count(0) {
+ for (int i = 0; i < NbEventQueue; i++)
+ lastReceived[i] = -1;
+ }
+ int lastReceived[NbEventQueue];
+ int count;
+ virtual void customEvent(QEvent* e) {
+ QVERIFY(e->type() >= QEvent::User);
+ QVERIFY(e->type() < QEvent::User + 5);
+ uint idx = e->type() - QEvent::User;
+ int value = static_cast<CustomEvent *>(e)->value;
+ QVERIFY(lastReceived[idx] < value);
+ lastReceived[idx] = value;
+ count++;
+ }
+
+ public slots:
+ void moveToThread(QThread *t) {
+ QObject::moveToThread(t);
+ }
+ };
+
+}
+
+void tst_QEventLoop::deliverInDefinedOrder_QTBUG19637()
+{
+ using namespace DeliverInDefinedOrder_QTBUG19637;
+ qMetaTypeId<QThread*>();
+ QThread threads[NbThread];
+ Object objects[NbObject];
+ for (int t = 0; t < NbThread; t++) {
+ threads[t].start();
+ }
+
+ int event = 0;
+
+ for (int o = 0; o < NbObject; o++) {
+ objects[o].moveToThread(&threads[o % NbThread]);
+ for (int e = 0; e < NbEvent; e++) {
+ int q = e % NbEventQueue;
+ QCoreApplication::postEvent(&objects[o], new CustomEvent(q, ++event) , q);
+ if (e % 7)
+ QMetaObject::invokeMethod(&objects[o], "moveToThread", Qt::QueuedConnection, Q_ARG(QThread*, &threads[(e+o)%NbThread]));
+ }
+ }
+
+ QTest::qWait(30);
+ for (int o = 0; o < NbObject; o++) {
+ QTRY_COMPARE(objects[o].count, int(NbEvent));
+ }
+
+ for (int t = 0; t < NbThread; t++) {
+ threads[t].quit();
+ threads[t].wait();
+ }
+
+}
+
+
+QTEST_MAIN(tst_QEventLoop)
+#include "tst_qeventloop.moc"
diff --git a/tests/auto/corelib/kernel/qitemmodel/.gitignore b/tests/auto/corelib/kernel/qitemmodel/.gitignore
new file mode 100644
index 0000000000..017be17fa8
--- /dev/null
+++ b/tests/auto/corelib/kernel/qitemmodel/.gitignore
@@ -0,0 +1 @@
+tst_qitemmodel
diff --git a/tests/auto/corelib/kernel/qitemmodel/README b/tests/auto/corelib/kernel/qitemmodel/README
new file mode 100644
index 0000000000..bddec0cef2
--- /dev/null
+++ b/tests/auto/corelib/kernel/qitemmodel/README
@@ -0,0 +1,3 @@
+This is a QStandardItemModel test. It will help catch a lot of simple problems. You should still create your own test for custom functionality and functions that your model has.
+
+Add your model to the modelstotest.cpp file (qt model's are included as examples) and modify the pro file accordingly. Fix the errors in order of failure as later tests assume the ones before them have passed.
diff --git a/tests/auto/corelib/kernel/qitemmodel/modelstotest.cpp b/tests/auto/corelib/kernel/qitemmodel/modelstotest.cpp
new file mode 100644
index 0000000000..82c28b5303
--- /dev/null
+++ b/tests/auto/corelib/kernel/qitemmodel/modelstotest.cpp
@@ -0,0 +1,424 @@
+/****************************************************************************
+**
+** 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 <QtCore/QtCore>
+#include <QtSql/QtSql>
+#include <QSortFilterProxyModel>
+
+/*
+ To add a model to be tested add the header file to the includes
+ and impliment what is needed in the four functions below.
+
+ You can add more then one model, several Qt models and included as examples.
+
+ In tst_qitemmodel.cpp a new ModelsToTest object is created for each test.
+
+ When you have errors fix the first ones first. Later tests depend upon them working
+*/
+
+class ModelsToTest {
+
+public:
+ ModelsToTest();
+
+ QAbstractItemModel *createModel(const QString &modelType);
+ QModelIndex populateTestArea(QAbstractItemModel *model);
+ void cleanupTestArea(QAbstractItemModel *model);
+
+ enum Read {
+ ReadOnly, // wont perform remove(), insert(), and setData()
+ ReadWrite
+ };
+ enum Contains {
+ Empty, // Confirm that rowCount() == 0 etc throughout the test
+ HasData // Confirm that rowCount() != 0 etc throughout the test
+ };
+
+ struct test {
+ test(QString m, Read r, Contains c) : modelType(m), read(r), contains(c){};
+
+ QString modelType;
+ Read read;
+ Contains contains;
+ };
+
+ QList<test> tests;
+
+ static void setupDatabase();
+};
+
+
+/*!
+ Add new tests, they can be the same model, but in a different state.
+
+ The name of the model is passed to createModel
+ If readOnly is true the remove tests will be skipped. Example: QDirModel is disabled.
+ If createModel returns an empty model. Example: QDirModel does not
+ */
+ModelsToTest::ModelsToTest()
+{
+ setupDatabase();
+
+ tests.append(test("QDirModel", ReadOnly, HasData));
+ tests.append(test("QStringListModel", ReadWrite, HasData));
+ tests.append(test("QStringListModelEmpty", ReadWrite, Empty));
+
+ tests.append(test("QStandardItemModel", ReadWrite, HasData));
+ tests.append(test("QStandardItemModelEmpty", ReadWrite, Empty));
+
+ // QSortFilterProxyModel test uses QStandardItemModel so test it first
+ tests.append(test("QSortFilterProxyModel", ReadWrite, HasData));
+ tests.append(test("QSortFilterProxyModelEmpty", ReadWrite, Empty));
+ tests.append(test("QSortFilterProxyModelRegExp", ReadWrite, HasData));
+
+ tests.append(test("QListModel", ReadWrite, HasData));
+ tests.append(test("QListModelEmpty", ReadWrite, Empty));
+ tests.append(test("QTableModel", ReadWrite, HasData));
+ tests.append(test("QTableModelEmpty", ReadWrite, Empty));
+
+ tests.append(test("QTreeModel", ReadWrite, HasData));
+ tests.append(test("QTreeModelEmpty", ReadWrite, Empty));
+
+ tests.append(test("QSqlQueryModel", ReadOnly, HasData));
+ tests.append(test("QSqlQueryModelEmpty", ReadOnly, Empty));
+
+ // Fails on remove
+ tests.append(test("QSqlTableModel", ReadOnly, HasData));
+}
+
+/*!
+ Return a new modelType.
+ */
+QAbstractItemModel *ModelsToTest::createModel(const QString &modelType)
+{
+ if (modelType == "QStringListModelEmpty")
+ return new QStringListModel();
+
+ if (modelType == "QStringListModel") {
+ QStringListModel *model = new QStringListModel();
+ populateTestArea(model);
+ return model;
+ }
+
+ if (modelType == "QStandardItemModelEmpty") {
+ return new QStandardItemModel();
+ }
+
+ if (modelType == "QStandardItemModel") {
+ QStandardItemModel *model = new QStandardItemModel();
+ populateTestArea(model);
+ return model;
+ }
+
+ if (modelType == "QSortFilterProxyModelEmpty") {
+ QSortFilterProxyModel *model = new QSortFilterProxyModel;
+ QStandardItemModel *standardItemModel = new QStandardItemModel;
+ model->setSourceModel(standardItemModel);
+ return model;
+ }
+
+ if (modelType == "QSortFilterProxyModelRegExp") {
+ QSortFilterProxyModel *model = new QSortFilterProxyModel;
+ QStandardItemModel *standardItemModel = new QStandardItemModel;
+ model->setSourceModel(standardItemModel);
+ populateTestArea(model);
+ model->setFilterRegExp(QRegExp("(^$|0.*)"));
+ return model;
+ }
+
+ if (modelType == "QSortFilterProxyModel") {
+ QSortFilterProxyModel *model = new QSortFilterProxyModel;
+ QStandardItemModel *standardItemModel = new QStandardItemModel;
+ model->setSourceModel(standardItemModel);
+ populateTestArea(model);
+ return model;
+ }
+
+ if (modelType == "QDirModel") {
+ QDirModel *model = new QDirModel();
+ model->setReadOnly(false);
+ return model;
+ }
+
+ if (modelType == "QSqlQueryModel") {
+ QSqlQueryModel *model = new QSqlQueryModel();
+ populateTestArea(model);
+ return model;
+ }
+
+ if (modelType == "QSqlQueryModelEmpty") {
+ QSqlQueryModel *model = new QSqlQueryModel();
+ return model;
+ }
+
+ if (modelType == "QSqlTableModel") {
+ QSqlTableModel *model = new QSqlTableModel();
+ populateTestArea(model);
+ return model;
+ }
+
+ if (modelType == "QListModelEmpty")
+ return (new QListWidget)->model();
+
+ if (modelType == "QListModel") {
+ QListWidget *widget = new QListWidget;
+ populateTestArea(widget->model());
+ return widget->model();
+ }
+
+ if (modelType == "QTableModelEmpty")
+ return (new QTableWidget)->model();
+
+ if (modelType == "QTableModel") {
+ QTableWidget *widget = new QTableWidget;
+ populateTestArea(widget->model());
+ return widget->model();
+ }
+
+ if (modelType == "QTreeModelEmpty") {
+ QTreeWidget *widget = new QTreeWidget;
+ return widget->model();
+ }
+
+ if (modelType == "QTreeModel") {
+ QTreeWidget *widget = new QTreeWidget;
+ populateTestArea(widget->model());
+ return widget->model();
+ }
+
+ return 0;
+}
+
+/*!
+ Fills model with some test data at least twenty rows and if possible twenty or more columns.
+ Return the parent of a row/column that can be tested.
+
+ NOTE: If readOnly is true tests will try to remove and add rows and columns.
+ If you have a tree model returning not the root index will help catch more errors.
+ */
+QModelIndex ModelsToTest::populateTestArea(QAbstractItemModel *model)
+{
+ if (QStringListModel *stringListModel = qobject_cast<QStringListModel *>(model)) {
+ QString alphabet = "a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z";
+ stringListModel->setStringList( alphabet.split(",") );
+ return QModelIndex();
+ }
+
+ if (qobject_cast<QStandardItemModel *>(model)) {
+ // Basic tree StandardItemModel
+ QModelIndex parent;
+ QVariant blue = QVariant(QColor(Qt::blue));
+#ifndef Q_OS_WINCE
+ for (int i = 0; i < 4; ++i) {
+#else
+ for (int i = 0; i < 2; ++i) {
+#endif
+ parent = model->index(0, 0, parent);
+ model->insertRows(0, 26 + i, parent);
+#ifndef Q_OS_WINCE
+ model->insertColumns(0, 26 + i, parent);
+#else
+ model->insertColumns(0, 4 + i, parent);
+#endif
+ // Fill in some values to make it easier to debug
+ /*
+ for (int x = 0; x < 26 + i; ++x) {
+ QString xval = QString::number(x);
+ for (int y = 0; y < 26 + i; ++y) {
+ QString val = xval + QString::number(y) + QString::number(i);
+ QModelIndex index = model->index(x, y, parent);
+ model->setData(index, val);
+ model->setData(index, blue, Qt::TextColorRole);
+ }
+ }
+ */
+ }
+ return model->index(0,0);
+ }
+
+ if (qobject_cast<QSortFilterProxyModel *>(model)) {
+ QAbstractItemModel *realModel = (qobject_cast<QSortFilterProxyModel *>(model))->sourceModel();
+ // Basic tree StandardItemModel
+ QModelIndex parent;
+ QVariant blue = QVariant(QColor(Qt::blue));
+#ifndef Q_OS_WINCE
+ for (int i = 0; i < 4; ++i) {
+#else
+ for (int i = 0; i < 2; ++i) {
+#endif
+ parent = realModel->index(0, 0, parent);
+ realModel->insertRows(0, 26+i, parent);
+#ifndef Q_OS_WINCE
+ realModel->insertColumns(0, 26+i, parent);
+#else
+ realModel->insertColumns(0, 4, parent);
+#endif
+ // Fill in some values to make it easier to debug
+ /*
+ for (int x = 0; x < 26+i; ++x) {
+ QString xval = QString::number(x);
+ for (int y = 0; y < 26 + i; ++y) {
+ QString val = xval + QString::number(y) + QString::number(i);
+ QModelIndex index = realModel->index(x, y, parent);
+ realModel->setData(index, val);
+ realModel->setData(index, blue, Qt::TextColorRole);
+ }
+ }
+ */
+ }
+ QModelIndex returnIndex = model->index(0,0);
+ if (!returnIndex.isValid())
+ qFatal("%s: model index to be returned is invalid", Q_FUNC_INFO);
+ return returnIndex;
+ }
+
+ if (QDirModel *dirModel = qobject_cast<QDirModel *>(model)) {
+ if (!QDir::current().mkdir("test"))
+ qFatal("%s: cannot create directory %s",
+ Q_FUNC_INFO,
+ qPrintable(QDir::toNativeSeparators(QDir::currentPath()+"/test")));
+ for (int i = 0; i < 26; ++i) {
+ QString subdir = QString("test/foo_%1").arg(i);
+ if (!QDir::current().mkdir(subdir))
+ qFatal("%s: cannot create directory %s",
+ Q_FUNC_INFO,
+ qPrintable(QDir::toNativeSeparators(QDir::currentPath()+"/"+subdir)));
+ }
+ return dirModel->index(QDir::currentPath()+"/test");
+ }
+
+ if (QSqlQueryModel *queryModel = qobject_cast<QSqlQueryModel *>(model)) {
+ QSqlQuery q;
+ q.exec("CREATE TABLE test(id int primary key, name varchar(30))");
+ q.prepare("INSERT INTO test(id, name) values (?, ?)");
+#ifndef Q_OS_WINCE
+ for (int i = 0; i < 1024; ++i) {
+#else
+ for (int i = 0; i < 512; ++i) {
+#endif
+ q.addBindValue(i);
+ q.addBindValue("Mr. Smith" + QString::number(i));
+ q.exec();
+ }
+ if (QSqlTableModel *tableModel = qobject_cast<QSqlTableModel *>(model)) {
+ tableModel->setTable("test");
+ tableModel->setEditStrategy(QSqlTableModel::OnManualSubmit);
+ tableModel->select();
+ } else {
+ queryModel->setQuery("select * from test");
+ }
+ return QModelIndex();
+ }
+
+ if (QListWidget *listWidget = qobject_cast<QListWidget *>(model->parent())) {
+#ifndef Q_OS_WINCE
+ int items = 100;
+#else
+ int items = 50;
+#endif
+ while (items--)
+ listWidget->addItem(QString("item %1").arg(items));
+ return QModelIndex();
+ }
+
+ if (QTableWidget *tableWidget = qobject_cast<QTableWidget *>(model->parent())) {
+ tableWidget->setColumnCount(20);
+ tableWidget->setRowCount(20);
+ return QModelIndex();
+ }
+
+ if (QTreeWidget *treeWidget = qobject_cast<QTreeWidget *>(model->parent())) {
+ int topItems = 20;
+ treeWidget->setColumnCount(1);
+ QTreeWidgetItem *parent;
+ while (topItems--){
+ parent = new QTreeWidgetItem(treeWidget, QStringList(QString("top %1").arg(topItems)));
+ for (int i = 0; i < 20; ++i)
+ new QTreeWidgetItem(parent, QStringList(QString("child %1").arg(topItems)));
+ }
+ return QModelIndex();
+ }
+
+ qFatal("%s: unknown type of model", Q_FUNC_INFO);
+ return QModelIndex();
+}
+
+/*!
+ If you need to cleanup from populateTest() do it here.
+ Note that this is called after every test even if populateTestArea isn't called.
+ */
+void ModelsToTest::cleanupTestArea(QAbstractItemModel *model)
+{
+ if (qobject_cast<QDirModel *>(model))
+ {
+ if (QDir(QDir::currentPath()+"/test").exists())
+ {
+ for (int i = 0; i < 26; ++i) {
+ QString subdir(QString("test/foo_%1").arg(i));
+ if (!QDir::current().rmdir(subdir))
+ qFatal("%s: cannot remove directory %s",
+ Q_FUNC_INFO,
+ qPrintable(QDir::toNativeSeparators(QDir::currentPath()+"/"+subdir)));
+ }
+ if (!QDir::current().rmdir("test"))
+ qFatal("%s: cannot remove directory %s",
+ Q_FUNC_INFO,
+ qPrintable(QDir::toNativeSeparators(QDir::currentPath()+"/test")));
+ }
+ } else if (qobject_cast<QSqlQueryModel *>(model)) {
+ QSqlQuery q("DROP TABLE test");
+ }
+}
+
+void ModelsToTest::setupDatabase()
+{
+ if (!QSqlDatabase::database().isValid()) {
+ QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
+ db.setDatabaseName(":memory:");
+ if (!db.open()) {
+ qWarning() << "Unable to open database" << db.lastError();
+ return;
+ }
+ }
+}
+
diff --git a/tests/auto/corelib/kernel/qitemmodel/qitemmodel.pro b/tests/auto/corelib/kernel/qitemmodel/qitemmodel.pro
new file mode 100644
index 0000000000..97d55fcd4d
--- /dev/null
+++ b/tests/auto/corelib/kernel/qitemmodel/qitemmodel.pro
@@ -0,0 +1,26 @@
+load(qttest_p4)
+SOURCES += tst_qitemmodel.cpp
+
+QT += widgets sql
+
+# NOTE: The deployment of the sqldrivers is disabled on purpose.
+# If we deploy the plugins, they are loaded twice when running
+# the tests on the autotest system. In that case we run out of
+# memory on Windows Mobile 5.
+
+#wince*: {
+# plugFiles.files = $$QT_BUILD_TREE/plugins/sqldrivers/*.dll
+# plugFiles.path = sqldrivers
+# DEPLOYMENT += plugFiles
+#}
+
+symbian {
+ TARGET.EPOCHEAPSIZE="0x100000 0x1000000" # // Min 1Mb, max 16Mb
+ qt_not_deployed {
+ contains(S60_VERSION, 3.1)|contains(S60_VERSION, 3.2)|contains(S60_VERSION, 5.0) {
+ sqlite.path = /sys/bin
+ sqlite.files = sqlite3.dll
+ DEPLOYMENT += sqlite
+ }
+ }
+}
diff --git a/tests/auto/corelib/kernel/qitemmodel/tst_qitemmodel.cpp b/tests/auto/corelib/kernel/qitemmodel/tst_qitemmodel.cpp
new file mode 100644
index 0000000000..191e9e88a9
--- /dev/null
+++ b/tests/auto/corelib/kernel/qitemmodel/tst_qitemmodel.cpp
@@ -0,0 +1,1410 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+
+/****************************************************************************
+**
+** 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.
+**
+** $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 particular 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 tries 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);
+ QVERIFY(currentModel);
+
+ 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);
+ QVERIFY(currentModel);
+
+ 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);
+ QVERIFY(currentModel);
+
+ 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);
+ QVERIFY(currentModel);
+
+ // 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);
+ QVERIFY(currentModel);
+
+ // 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 useful 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);
+ QVERIFY(currentModel);
+
+ // 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 column 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);
+ QVERIFY(currentModel);
+
+ // 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);
+ QVERIFY(currentModel);
+ 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);
+ QVERIFY(currentModel);
+
+ 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);
+ QVERIFY(currentModel);
+
+ 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, haven'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);
+ QVERIFY(currentModel);
+
+ 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 emitted.
+ 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 emitted and get the row/column count when they do get emitted 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 emitted
+ 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 emitted
+ //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 emitted
+ 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, haven'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);
+ QVERIFY(currentModel);
+
+ 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 emitted.
+ 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 emitted and get the row/column count when they do get emitted 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 emitted
+ 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 emitted
+ //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 emitted
+ 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"
diff --git a/tests/auto/corelib/kernel/qmath/qmath.pro b/tests/auto/corelib/kernel/qmath/qmath.pro
new file mode 100644
index 0000000000..e5784ce7aa
--- /dev/null
+++ b/tests/auto/corelib/kernel/qmath/qmath.pro
@@ -0,0 +1,7 @@
+load(qttest_p4)
+
+QT = core
+
+SOURCES += tst_qmath.cpp
+QT = core
+CONFIG += parallel_test
diff --git a/tests/auto/corelib/kernel/qmath/tst_qmath.cpp b/tests/auto/corelib/kernel/qmath/tst_qmath.cpp
new file mode 100644
index 0000000000..f908bd6e3e
--- /dev/null
+++ b/tests/auto/corelib/kernel/qmath/tst_qmath.cpp
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** 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 <qmath.h>
+
+static const qreal PI = 3.14159265358979323846264338327950288;
+
+class tst_QMath : public QObject
+{
+ Q_OBJECT
+private slots:
+ void fastSinCos();
+};
+
+void tst_QMath::fastSinCos()
+{
+ // Test evenly spaced angles from 0 to 2pi radians.
+ const int LOOP_COUNT = 100000;
+ for (int i = 0; i < LOOP_COUNT; ++i) {
+ qreal angle = i * 2 * PI / (LOOP_COUNT - 1);
+ QVERIFY(qAbs(qSin(angle) - qFastSin(angle)) < 1e-5);
+ QVERIFY(qAbs(qCos(angle) - qFastCos(angle)) < 1e-5);
+ }
+}
+
+QTEST_APPLESS_MAIN(tst_QMath)
+
+#include "tst_qmath.moc"
diff --git a/tests/auto/corelib/kernel/qmetaobject/.gitignore b/tests/auto/corelib/kernel/qmetaobject/.gitignore
new file mode 100644
index 0000000000..c5b38e7816
--- /dev/null
+++ b/tests/auto/corelib/kernel/qmetaobject/.gitignore
@@ -0,0 +1 @@
+tst_qmetaobject
diff --git a/tests/auto/corelib/kernel/qmetaobject/qmetaobject.pro b/tests/auto/corelib/kernel/qmetaobject/qmetaobject.pro
new file mode 100644
index 0000000000..7a4cf94e47
--- /dev/null
+++ b/tests/auto/corelib/kernel/qmetaobject/qmetaobject.pro
@@ -0,0 +1,8 @@
+load(qttest_p4)
+
+QT = core gui widgets
+
+SOURCES += tst_qmetaobject.cpp
+
+
+
diff --git a/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp
new file mode 100644
index 0000000000..a813a917d7
--- /dev/null
+++ b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp
@@ -0,0 +1,1120 @@
+/****************************************************************************
+**
+** 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 <qobject.h>
+#include <qmetaobject.h>
+#include <qlabel.h>
+
+//TESTED_CLASS=
+//TESTED_FILES=
+
+struct MyStruct
+{
+ int i;
+};
+
+namespace MyNamespace {
+ class MyClass : public QObject
+ {
+ Q_OBJECT
+ Q_PROPERTY(MyEnum myEnum READ myEnum WRITE setMyEnum)
+ Q_PROPERTY(MyFlags myFlags READ myFlags WRITE setMyFlags)
+
+ Q_ENUMS(MyEnum)
+ Q_FLAGS(MyFlags)
+ public:
+ enum MyEnum {
+ MyEnum1,
+ MyEnum2,
+ MyEnum3
+ };
+
+ enum MyFlag {
+ MyFlag1 = 0x01,
+ MyFlag2 = 0x02,
+ MyFlag3 = 0x04
+ };
+ Q_DECLARE_FLAGS(MyFlags, MyFlag)
+
+ MyEnum myEnum() const { return m_enum; }
+ void setMyEnum(MyEnum val) { m_enum = val; }
+
+ MyFlags myFlags() const { return m_flags; }
+ void setMyFlags(MyFlags val) { m_flags = val; }
+
+ MyClass(QObject *parent = 0)
+ : QObject(parent),
+ m_enum(MyEnum1),
+ m_flags(MyFlag1|MyFlag2)
+ { }
+ private:
+ MyEnum m_enum;
+ MyFlags m_flags;
+ };
+ Q_DECLARE_OPERATORS_FOR_FLAGS(MyClass::MyFlags)
+}
+
+
+class tst_QMetaObject : public QObject
+{
+ Q_OBJECT
+ Q_ENUMS(EnumType)
+ Q_PROPERTY(EnumType value WRITE setValue READ getValue)
+ Q_PROPERTY(EnumType value2 WRITE set_value READ get_value)
+ Q_PROPERTY(MyStruct value3 WRITE setVal3 READ val3)
+ Q_PROPERTY(QList<QVariant> value4 WRITE setVal4 READ val4)
+ Q_PROPERTY(QVariantList value5 WRITE setVal5 READ val5)
+ Q_PROPERTY(int value6 READ value6 NOTIFY value6Changed)
+ Q_PROPERTY(MyStruct value7 READ value7 WRITE setVal7 NOTIFY value7Changed)
+ Q_PROPERTY(int value8 READ value8)
+ Q_PROPERTY(int value9 READ value9 CONSTANT)
+ Q_PROPERTY(int value10 READ value10 FINAL)
+
+public:
+ enum EnumType { EnumType1 };
+
+ tst_QMetaObject();
+ ~tst_QMetaObject();
+
+ void setValue(EnumType) {}
+ EnumType getValue() const { return EnumType1; }
+
+ void set_value(EnumType) {}
+ EnumType get_value() const { return EnumType1; }
+
+ void setVal3(MyStruct) {}
+ MyStruct val3() const { MyStruct s = {42}; return s; }
+
+ void setVal4(const QList<QVariant> &list) { value4 = list; }
+ QList<QVariant> val4() const { return value4; }
+
+ void setVal5(const QVariantList &list) { value5 = list; }
+ QVariantList val5() const { return value5; }
+
+ int value6() const { return 1; }
+
+ void setVal7(MyStruct) {}
+ MyStruct value7() const { MyStruct s = {42}; return s; }
+
+ int value8() const { return 1; }
+
+ int value9() const { return 1; }
+
+ int value10() const { return 1; }
+
+ QList<QVariant> value4;
+ QVariantList value5;
+
+public slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+private slots:
+ void connectSlotsByName();
+ void invokeMetaMember();
+ void invokeQueuedMetaMember();
+ void invokeBlockingQueuedMetaMember();
+ void invokeCustomTypes();
+ void invokeMetaConstructor();
+ void invokeTypedefTypes();
+ void qtMetaObjectInheritance();
+ void normalizedSignature_data();
+ void normalizedSignature();
+ void normalizedType_data();
+ void normalizedType();
+ void customPropertyType();
+ void checkScope();
+ void propertyNotify();
+ void propertyConstant();
+ void propertyFinal();
+
+ void stdSet();
+ void classInfo();
+
+ void metaMethod();
+
+ void indexOfMethod_data();
+ void indexOfMethod();
+
+signals:
+ void value6Changed();
+ void value7Changed(const QString &);
+};
+
+tst_QMetaObject::tst_QMetaObject()
+{
+
+}
+
+tst_QMetaObject::~tst_QMetaObject()
+{
+
+}
+
+void tst_QMetaObject::initTestCase()
+{
+}
+
+void tst_QMetaObject::cleanupTestCase()
+{
+}
+
+void tst_QMetaObject::init()
+{
+}
+
+void tst_QMetaObject::cleanup()
+{
+}
+
+void tst_QMetaObject::stdSet()
+{
+ const QMetaObject *mo = metaObject();
+
+ QMetaProperty prop = mo->property(mo->indexOfProperty("value"));
+ QVERIFY(prop.isValid());
+ QVERIFY(prop.hasStdCppSet());
+
+ prop = mo->property(mo->indexOfProperty("value2"));
+ QVERIFY(prop.isValid());
+ QVERIFY(!prop.hasStdCppSet());
+}
+
+class CTestObject: public QObject
+{
+ Q_OBJECT
+
+public:
+ CTestObject(): QObject(), invokeCount1(0), invokeCount2(0)
+ {
+ }
+
+ void fire(const QString &name)
+ {
+ child = new QObject(this);
+ child->setObjectName(name);
+ QMetaObject::connectSlotsByName(this);
+ delete child; child = 0;
+ }
+
+ int invokeCount1;
+ int invokeCount2;
+ QObject *child;
+
+public slots:
+ void on_child1_destroyed(QObject *obj = 0)
+ {
+ ++invokeCount1;
+ if (!obj || obj != child)
+ qWarning() << "on_child1_destroyed invoked with wrong child object";
+ }
+ void on_child2_destroyed() { ++invokeCount2; }
+};
+
+class CTestObjectOverloads: public QObject
+{
+ Q_OBJECT
+
+public:
+ CTestObjectOverloads(): invokeCount1(0), invokeCount2(0) {}
+
+ int invokeCount1;
+ int invokeCount2;
+ QObject *child;
+
+ void fire(const QString &name)
+ {
+ child = new QObject(this);
+ child->setObjectName(name);
+ QMetaObject::connectSlotsByName(this);
+ delete child; child = 0;
+ }
+
+private slots:
+ void on_child1_destroyed(QObject *obj)
+ {
+ ++invokeCount1;
+ if (!obj || obj != child)
+ qWarning() << "on_child1_destroyed invoked with wrong child object";
+ }
+ void on_child1_destroyed() { ++invokeCount2; }
+};
+
+#define FUNCTION(x) "QMetaObject::" x ": "
+
+void tst_QMetaObject::connectSlotsByName()
+{
+ CTestObject obj;
+ QCOMPARE(obj.invokeCount1, 0);
+ QCOMPARE(obj.invokeCount2, 0);
+
+ QTest::ignoreMessage(QtWarningMsg, FUNCTION("connectSlotsByName") "No matching signal for on_child1_destroyed(QObject*)");
+ QTest::ignoreMessage(QtWarningMsg, FUNCTION("connectSlotsByName") "No matching signal for on_child2_destroyed()");
+ obj.fire("bubu");
+ QCOMPARE(obj.invokeCount1, 0);
+ QCOMPARE(obj.invokeCount2, 0);
+
+ QTest::ignoreMessage(QtWarningMsg, FUNCTION("connectSlotsByName") "No matching signal for on_child2_destroyed()");
+ obj.fire("child1");
+ QCOMPARE(obj.invokeCount1, 1);
+ QCOMPARE(obj.invokeCount2, 0);
+
+ QTest::ignoreMessage(QtWarningMsg, FUNCTION("connectSlotsByName") "No matching signal for on_child1_destroyed(QObject*)");
+ obj.fire("child2");
+ QCOMPARE(obj.invokeCount1, 1);
+ QCOMPARE(obj.invokeCount2, 1);
+
+ QTest::ignoreMessage(QtWarningMsg, FUNCTION("connectSlotsByName") "No matching signal for on_child2_destroyed()");
+ obj.fire("child1");
+ QCOMPARE(obj.invokeCount1, 2);
+ QCOMPARE(obj.invokeCount2, 1);
+
+ // now test with real overloads
+ CTestObjectOverloads obj2;
+ obj2.fire("child1");
+ QCOMPARE(obj2.invokeCount1, 1);
+ QCOMPARE(obj2.invokeCount2, 1);
+}
+
+class QtTestObject: public QObject
+{
+ Q_OBJECT
+
+public:
+ QtTestObject();
+ Q_INVOKABLE QtTestObject(QObject *parent);
+
+public slots:
+ void sl0();
+ QString sl1(QString s1);
+ void sl2(QString s1, QString s2);
+ void sl3(QString s1, QString s2, QString s3);
+ void sl4(QString s1, QString s2, QString s3, const QString s4);
+ void sl5(QString s1, QString s2, QString s3, QString s4, const QString &s5);
+ void sl6(QString s1, QString s2, QString s3, QString s4, const QString s5, QString s6);
+ void sl7(QString s1, QString s2, QString s3, QString s4, QString s5, QString s6, QString s7);
+ void sl8(QString s1, QString s2, QString s3, QString s4, QString s5, QString s6, QString s7,
+ QString s8);
+ void sl9(QString s1, QString s2, QString s3, QString s4, QString s5, QString s6, QString s7,
+ QString s8, QString s9);
+ void sl10(QString s1, QString s2, QString s3, QString s4, QString s5, QString s6, QString s7,
+ QString s8, QString s9, QString s10);
+ QObject *sl11();
+ const char *sl12();
+ QList<QString> sl13(QList<QString> l1);
+ void testSender();
+
+ void testReference(QString &str);
+
+ void testLongLong(qint64 ll1, quint64 ll2);
+
+ void moveToThread(QThread *t)
+ { QObject::moveToThread(t); }
+
+signals:
+ void sig0();
+ QString sig1(QString s1);
+
+protected:
+ QtTestObject(QVariant) {}
+private:
+ QtTestObject(QVariant, QVariant) {}
+
+public:
+ QString slotResult;
+};
+
+QtTestObject::QtTestObject()
+{
+ connect(this, SIGNAL(sig0()), this, SLOT(sl0()));
+ connect(this, SIGNAL(sig1(QString)), this, SLOT(sl1(QString)));
+}
+
+QtTestObject::QtTestObject(QObject *parent)
+ : QObject(parent)
+{
+}
+
+void QtTestObject::sl0() { slotResult = "sl0"; };
+QString QtTestObject::sl1(QString s1) { slotResult = "sl1:" + s1; return "yessir"; }
+void QtTestObject::sl2(QString s1, QString s2) { slotResult = "sl2:" + s1 + s2; }
+void QtTestObject::sl3(QString s1, QString s2, QString s3)
+{ slotResult = "sl3:" + s1 + s2 + s3; }
+void QtTestObject::sl4(QString s1, QString s2, QString s3, const QString s4)
+{ slotResult = "sl4:" + s1 + s2 + s3 + s4; }
+void QtTestObject::sl5(QString s1, QString s2, QString s3, QString s4, const QString &s5)
+{ slotResult = "sl5:" + s1 + s2 + s3 + s4 + s5; }
+void QtTestObject::sl6(QString s1, QString s2, QString s3, QString s4,
+ const QString s5, QString s6)
+{ slotResult = "sl6:" + s1 + s2 + s3 + s4 + s5 + s6; }
+void QtTestObject::sl7(QString s1, QString s2, QString s3, QString s4, QString s5,
+ QString s6, QString s7)
+{ slotResult = "sl7:" + s1 + s2 + s3 + s4 + s5 + s6 + s7; }
+void QtTestObject::sl8(QString s1, QString s2, QString s3, QString s4, QString s5,
+ QString s6, QString s7, QString s8)
+{ slotResult = "sl8:" + s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8; }
+void QtTestObject::sl9(QString s1, QString s2, QString s3, QString s4, QString s5,
+ QString s6, QString s7, QString s8, QString s9)
+{ slotResult = "sl9:" + s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9; }
+void QtTestObject::sl10(QString s1, QString s2, QString s3, QString s4, QString s5,
+ QString s6, QString s7, QString s8, QString s9, QString s10)
+{ slotResult = "sl10:" + s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10; }
+QObject *QtTestObject::sl11()
+{ slotResult = "sl11"; return this; }
+const char *QtTestObject::sl12()
+{ slotResult = "sl12"; return "foo"; }
+QList<QString> QtTestObject::sl13(QList<QString> l1)
+{ slotResult = "sl13"; return l1; }
+void QtTestObject::testReference(QString &str)
+{ slotResult = "testReference:" + str; str = "gotcha"; }
+
+void QtTestObject::testLongLong(qint64 ll1, quint64 ll2)
+{ slotResult = "testLongLong:" + QString::number(ll1) + "," + QString::number(ll2); }
+
+void QtTestObject::testSender()
+{
+ slotResult.sprintf("%p", sender());
+}
+
+
+void tst_QMetaObject::invokeMetaMember()
+{
+ QtTestObject obj;
+
+ QString t1("1"); QString t2("2"); QString t3("3"); QString t4("4"); QString t5("5");
+ QString t6("6"); QString t7("7"); QString t8("8"); QString t9("9"); QString t10("X");
+
+ QVERIFY(!QMetaObject::invokeMethod(0, 0));
+ QVERIFY(!QMetaObject::invokeMethod(0, "sl0"));
+ QVERIFY(!QMetaObject::invokeMethod(&obj, 0));
+
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sl0"));
+ QCOMPARE(obj.slotResult, QString("sl0"));
+
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sl1", Q_ARG(QString, t1)));
+ QCOMPARE(obj.slotResult, QString("sl1:1"));
+
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sl2", Q_ARG(const QString, t1), Q_ARG(QString, t2)));
+ QCOMPARE(obj.slotResult, QString("sl2:12"));
+
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sl3", Q_ARG(QString, t1), Q_ARG(QString, t2), Q_ARG(QString, t3)));
+ QCOMPARE(obj.slotResult, QString("sl3:123"));
+
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sl4", Q_ARG(QString, t1), Q_ARG(QString, t2), Q_ARG(QString, t3),
+ Q_ARG(QString, t4)));
+ QCOMPARE(obj.slotResult, QString("sl4:1234"));
+
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sl5", Q_ARG(QString, t1), Q_ARG(QString, t2), Q_ARG(QString, t3),
+ Q_ARG(QString, t4), Q_ARG(QString, "5")));
+ QCOMPARE(obj.slotResult, QString("sl5:12345"));
+
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sl6", Q_ARG(QString, t1), Q_ARG(QString, t2), Q_ARG(QString, t3),
+ Q_ARG(QString, t4), Q_ARG(QString, t5), Q_ARG(QString, t6)));
+ QCOMPARE(obj.slotResult, QString("sl6:123456"));
+
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sl7", Q_ARG(QString, t1), Q_ARG(QString, t2), Q_ARG(QString, t3),
+ Q_ARG(QString, t4), Q_ARG(QString, t5), Q_ARG(QString, t6),
+ Q_ARG(QString, t7)));
+ QCOMPARE(obj.slotResult, QString("sl7:1234567"));
+
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sl8", Q_ARG(QString, t1), Q_ARG(QString, t2), Q_ARG(QString, t3),
+ Q_ARG(QString, t4), Q_ARG(QString, t5), Q_ARG(QString, t6),
+ Q_ARG(QString, t7), Q_ARG(QString, t8)));
+ QCOMPARE(obj.slotResult, QString("sl8:12345678"));
+
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sl9", Q_ARG(QString, t1), Q_ARG(QString, t2), Q_ARG(QString, t3),
+ Q_ARG(QString, t4), Q_ARG(QString, t5), Q_ARG(QString, t6),
+ Q_ARG(QString, t7), Q_ARG(QString, t8), Q_ARG(QString, t9)));
+ QCOMPARE(obj.slotResult, QString("sl9:123456789"));
+
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sl11"));
+ QCOMPARE(obj.slotResult, QString("sl11"));
+
+ QVERIFY(QMetaObject::invokeMethod(&obj, "testSender"));
+ QCOMPARE(obj.slotResult, QString("0x0"));
+
+ QString refStr("whatever");
+ QVERIFY(QMetaObject::invokeMethod(&obj, "testReference", QGenericArgument("QString&", &refStr)));
+ QCOMPARE(obj.slotResult, QString("testReference:whatever"));
+ QCOMPARE(refStr, QString("gotcha"));
+
+ qint64 ll1 = -1;
+ quint64 ll2 = 0;
+ QVERIFY(QMetaObject::invokeMethod(&obj,
+ "testLongLong",
+ Q_ARG(qint64, ll1),
+ Q_ARG(quint64, ll2)));
+ QCOMPARE(obj.slotResult, QString("testLongLong:-1,0"));
+
+ QString exp;
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sl1", Q_RETURN_ARG(QString, exp), Q_ARG(QString, "bubu")));
+ QCOMPARE(exp, QString("yessir"));
+ QCOMPARE(obj.slotResult, QString("sl1:bubu"));
+
+ QObject *ptr = 0;
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sl11", Q_RETURN_ARG(QObject*,ptr)));
+ QCOMPARE(ptr, (QObject *)&obj);
+ QCOMPARE(obj.slotResult, QString("sl11"));
+ // try again with a space:
+ ptr = 0;
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sl11", Q_RETURN_ARG(QObject * , ptr)));
+ QCOMPARE(ptr, (QObject *)&obj);
+ QCOMPARE(obj.slotResult, QString("sl11"));
+
+ const char *ptr2 = 0;
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sl12", Q_RETURN_ARG(const char*, ptr2)));
+ QVERIFY(ptr2 != 0);
+ QCOMPARE(obj.slotResult, QString("sl12"));
+ // try again with a space:
+ ptr2 = 0;
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sl12", Q_RETURN_ARG(char const * , ptr2)));
+ QVERIFY(ptr2 != 0);
+ QCOMPARE(obj.slotResult, QString("sl12"));
+
+ // test w/ template args
+ QList<QString> returnValue, argument;
+ argument << QString("one") << QString("two") << QString("three");
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sl13",
+ Q_RETURN_ARG(QList<QString>, returnValue),
+ Q_ARG(QList<QString>, argument)));
+ QCOMPARE(returnValue, argument);
+ QCOMPARE(obj.slotResult, QString("sl13"));
+
+ //test signals
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sig0"));
+ QCOMPARE(obj.slotResult, QString("sl0"));
+
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sig1", Q_ARG(QString, "baba")));
+ QCOMPARE(obj.slotResult, QString("sl1:baba"));
+
+ exp.clear();
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sig1", Q_RETURN_ARG(QString, exp), Q_ARG(QString, "hehe")));
+ QCOMPARE(exp, QString("yessir"));
+ QCOMPARE(obj.slotResult, QString("sl1:hehe"));
+
+ QTest::ignoreMessage(QtWarningMsg, "QMetaObject::invokeMethod: No such method QtTestObject::doesNotExist()");
+ QVERIFY(!QMetaObject::invokeMethod(&obj, "doesNotExist"));
+ QTest::ignoreMessage(QtWarningMsg, "QMetaObject::invokeMethod: No such method QtTestObject::sl1(QString)(QString)");
+ QVERIFY(!QMetaObject::invokeMethod(&obj, "sl1(QString)", Q_ARG(QString, "arg")));
+ QTest::ignoreMessage(QtWarningMsg, "QMetaObject::invokeMethod: No such method QtTestObject::sl3(QString)");
+ QVERIFY(!QMetaObject::invokeMethod(&obj, "sl3", Q_ARG(QString, "arg")));
+ QTest::ignoreMessage(QtWarningMsg, "QMetaObject::invokeMethod: No such method QtTestObject::sl1(QString,QString,QString)");
+ QVERIFY(!QMetaObject::invokeMethod(&obj, "sl1", Q_ARG(QString, "arg"), Q_ARG(QString, "arg"), Q_ARG(QString, "arg")));
+
+ //should not have changed since last test.
+ QCOMPARE(exp, QString("yessir"));
+ QCOMPARE(obj.slotResult, QString("sl1:hehe"));
+}
+
+void tst_QMetaObject::invokeQueuedMetaMember()
+{
+ QtTestObject obj;
+
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sl0", Qt::QueuedConnection));
+ QVERIFY(obj.slotResult.isEmpty());
+ qApp->processEvents(QEventLoop::AllEvents);
+ QCOMPARE(obj.slotResult, QString("sl0"));
+ obj.slotResult = QString();
+
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sl1", Qt::QueuedConnection, Q_ARG(QString, QString("hallo"))));
+ QVERIFY(obj.slotResult.isEmpty());
+ qApp->processEvents(QEventLoop::AllEvents);
+ QCOMPARE(obj.slotResult, QString("sl1:hallo"));
+ obj.slotResult = QString();
+
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sl9", Qt::QueuedConnection, Q_ARG(QString, "1"), Q_ARG(QString, "2"),
+ Q_ARG(QString, "3"), Q_ARG(QString, "4"), Q_ARG(QString, "5"),
+ Q_ARG(QString, "6"), Q_ARG(QString, "7"), Q_ARG(QString, "8"),
+ Q_ARG(QString, "9")));
+ QVERIFY(obj.slotResult.isEmpty());
+ qApp->processEvents(QEventLoop::AllEvents);
+ QCOMPARE(obj.slotResult, QString("sl9:123456789"));
+
+ // signals
+
+ obj.slotResult.clear();
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sig0", Qt::QueuedConnection));
+ QVERIFY(obj.slotResult.isEmpty());
+ qApp->processEvents(QEventLoop::AllEvents);
+ QCOMPARE(obj.slotResult, QString("sl0"));
+
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sig1", Qt::QueuedConnection, Q_ARG(QString, "gogo")));
+ qApp->processEvents(QEventLoop::AllEvents);
+ QCOMPARE(obj.slotResult, QString("sl1:gogo"));
+
+ QString exp;
+ QTest::ignoreMessage(QtWarningMsg, "QMetaMethod::invoke: Unable to invoke methods with return values in queued connections");
+ QVERIFY(!QMetaObject::invokeMethod(&obj, "sig1", Qt::QueuedConnection, Q_RETURN_ARG(QString, exp),
+ Q_ARG(QString, "nono")));
+
+ qint64 ll1 = -1;
+ quint64 ll2 = 0;
+ QVERIFY(QMetaObject::invokeMethod(&obj,
+ "testLongLong",
+ Qt::QueuedConnection,
+ Q_ARG(qint64, ll1),
+ Q_ARG(quint64, ll2)));
+ qApp->processEvents(QEventLoop::AllEvents);
+ QCOMPARE(obj.slotResult, QString("testLongLong:-1,0"));
+}
+
+void tst_QMetaObject::invokeBlockingQueuedMetaMember()
+{
+ QThread t;
+ t.start();
+ QtTestObject obj;
+ obj.moveToThread(&t);
+
+ QString t1("1"); QString t2("2"); QString t3("3"); QString t4("4"); QString t5("5");
+ QString t6("6"); QString t7("7"); QString t8("8"); QString t9("9"); QString t10("X");
+
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sl1", Qt::BlockingQueuedConnection, Q_ARG(QString, t1)));
+ QCOMPARE(obj.slotResult, QString("sl1:1"));
+
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sl2", Qt::BlockingQueuedConnection, Q_ARG(const QString, t1), Q_ARG(QString, t2)));
+ QCOMPARE(obj.slotResult, QString("sl2:12"));
+
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sl3", Qt::BlockingQueuedConnection, Q_ARG(QString, t1), Q_ARG(QString, t2), Q_ARG(QString, t3)));
+ QCOMPARE(obj.slotResult, QString("sl3:123"));
+
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sl4", Qt::BlockingQueuedConnection, Q_ARG(QString, t1), Q_ARG(QString, t2),
+ Q_ARG(QString, t3), Q_ARG(QString, t4)));
+ QCOMPARE(obj.slotResult, QString("sl4:1234"));
+
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sl5", Qt::BlockingQueuedConnection, Q_ARG(QString, t1), Q_ARG(QString, t2),
+ Q_ARG(QString, t3), Q_ARG(QString, t4), Q_ARG(QString, "5")));
+ QCOMPARE(obj.slotResult, QString("sl5:12345"));
+
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sl6", Qt::BlockingQueuedConnection, Q_ARG(QString, t1), Q_ARG(QString, t2),
+ Q_ARG(QString, t3), Q_ARG(QString, t4), Q_ARG(QString, t5), Q_ARG(QString, t6)));
+ QCOMPARE(obj.slotResult, QString("sl6:123456"));
+
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sl7", Qt::BlockingQueuedConnection, Q_ARG(QString, t1), Q_ARG(QString, t2),
+ Q_ARG(QString, t3), Q_ARG(QString, t4), Q_ARG(QString, t5), Q_ARG(QString, t6),
+ Q_ARG(QString, t7)));
+ QCOMPARE(obj.slotResult, QString("sl7:1234567"));
+
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sl8", Qt::BlockingQueuedConnection, Q_ARG(QString, t1), Q_ARG(QString, t2),
+ Q_ARG(QString, t3), Q_ARG(QString, t4), Q_ARG(QString, t5), Q_ARG(QString, t6),
+ Q_ARG(QString, t7), Q_ARG(QString, t8)));
+ QCOMPARE(obj.slotResult, QString("sl8:12345678"));
+
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sl9", Qt::BlockingQueuedConnection, Q_ARG(QString, t1), Q_ARG(QString, t2),
+ Q_ARG(QString, t3), Q_ARG(QString, t4), Q_ARG(QString, t5), Q_ARG(QString, t6),
+ Q_ARG(QString, t7), Q_ARG(QString, t8), Q_ARG(QString, t9)));
+ QCOMPARE(obj.slotResult, QString("sl9:123456789"));
+
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sl11", Qt::BlockingQueuedConnection));
+ QCOMPARE(obj.slotResult, QString("sl11"));
+
+ QVERIFY(QMetaObject::invokeMethod(&obj, "testSender", Qt::BlockingQueuedConnection));
+ QCOMPARE(obj.slotResult, QString("0x0"));
+
+ QString refStr("whatever");
+ QVERIFY(QMetaObject::invokeMethod(&obj, "testReference", Qt::BlockingQueuedConnection, QGenericArgument("QString&", &refStr)));
+ QCOMPARE(obj.slotResult, QString("testReference:whatever"));
+ QCOMPARE(refStr, QString("gotcha"));
+
+ qint64 ll1 = -1;
+ quint64 ll2 = 0;
+ QVERIFY(QMetaObject::invokeMethod(&obj,
+ "testLongLong",
+ Qt::BlockingQueuedConnection,
+ Q_ARG(qint64, ll1),
+ Q_ARG(quint64, ll2)));
+ QCOMPARE(obj.slotResult, QString("testLongLong:-1,0"));
+
+ QString exp;
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sl1", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QString, exp), Q_ARG(QString, "bubu")));
+ QCOMPARE(exp, QString("yessir"));
+ QCOMPARE(obj.slotResult, QString("sl1:bubu"));
+
+ QObject *ptr = 0;
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sl11", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QObject*,ptr)));
+ QCOMPARE(ptr, (QObject *)&obj);
+ QCOMPARE(obj.slotResult, QString("sl11"));
+ // try again with a space:
+ ptr = 0;
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sl11", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QObject * , ptr)));
+ QCOMPARE(ptr, (QObject *)&obj);
+ QCOMPARE(obj.slotResult, QString("sl11"));
+
+ const char *ptr2 = 0;
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sl12", Qt::BlockingQueuedConnection, Q_RETURN_ARG(const char*, ptr2)));
+ QVERIFY(ptr2 != 0);
+ QCOMPARE(obj.slotResult, QString("sl12"));
+ // try again with a space:
+ ptr2 = 0;
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sl12", Qt::BlockingQueuedConnection, Q_RETURN_ARG(char const * , ptr2)));
+ QVERIFY(ptr2 != 0);
+ QCOMPARE(obj.slotResult, QString("sl12"));
+
+ // test w/ template args
+ QList<QString> returnValue, argument;
+ argument << QString("one") << QString("two") << QString("three");
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sl13", Qt::BlockingQueuedConnection,
+ Q_RETURN_ARG(QList<QString>, returnValue),
+ Q_ARG(QList<QString>, argument)));
+ QCOMPARE(returnValue, argument);
+ QCOMPARE(obj.slotResult, QString("sl13"));
+
+ //test signals
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sig0", Qt::BlockingQueuedConnection));
+ QCOMPARE(obj.slotResult, QString("sl0"));
+
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sig1", Qt::BlockingQueuedConnection, Q_ARG(QString, "baba")));
+ QCOMPARE(obj.slotResult, QString("sl1:baba"));
+
+ exp.clear();
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sig1", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QString, exp), Q_ARG(QString, "hehe")));
+ QCOMPARE(exp, QString("yessir"));
+ QCOMPARE(obj.slotResult, QString("sl1:hehe"));
+
+ QTest::ignoreMessage(QtWarningMsg, "QMetaObject::invokeMethod: No such method QtTestObject::doesNotExist()");
+ QVERIFY(!QMetaObject::invokeMethod(&obj, "doesNotExist", Qt::BlockingQueuedConnection));
+ QTest::ignoreMessage(QtWarningMsg, "QMetaObject::invokeMethod: No such method QtTestObject::sl1(QString)(QString)");
+ QVERIFY(!QMetaObject::invokeMethod(&obj, "sl1(QString)", Qt::BlockingQueuedConnection, Q_ARG(QString, "arg")));
+ QTest::ignoreMessage(QtWarningMsg, "QMetaObject::invokeMethod: No such method QtTestObject::sl3(QString)");
+ QVERIFY(!QMetaObject::invokeMethod(&obj, "sl3", Qt::BlockingQueuedConnection, Q_ARG(QString, "arg")));
+ QTest::ignoreMessage(QtWarningMsg, "QMetaObject::invokeMethod: No such method QtTestObject::sl1(QString,QString,QString)");
+ QVERIFY(!QMetaObject::invokeMethod(&obj, "sl1", Qt::BlockingQueuedConnection, Q_ARG(QString, "arg"), Q_ARG(QString, "arg"), Q_ARG(QString, "arg")));
+
+ //should not have changed since last test.
+ QCOMPARE(exp, QString("yessir"));
+ QCOMPARE(obj.slotResult, QString("sl1:hehe"));
+
+ QVERIFY(QMetaObject::invokeMethod(&obj, "moveToThread", Qt::BlockingQueuedConnection, Q_ARG(QThread*, QThread::currentThread())));
+ t.quit();
+ QVERIFY(t.wait());
+
+}
+
+
+
+void tst_QMetaObject::qtMetaObjectInheritance()
+{
+ QVERIFY(QObject::staticMetaObject.superClass() == 0);
+ QCOMPARE(QLabel::staticMetaObject.indexOfEnumerator("Qt::Alignment"), -1);
+ QCOMPARE(QLabel::staticMetaObject.indexOfEnumerator("Alignment"), -1);
+ int indexOfAlignment = QLabel::staticMetaObject.indexOfProperty("alignment");
+ QVERIFY(indexOfAlignment != -1);
+ QMetaProperty alignment = QLabel::staticMetaObject.property(indexOfAlignment);
+ QVERIFY(alignment.isValid());
+ QCOMPARE(alignment.enumerator().name(), "Alignment");
+}
+
+struct MyType
+{
+ int i1, i2, i3;
+};
+
+typedef QString CustomString;
+
+class QtTestCustomObject: public QObject
+{
+ Q_OBJECT
+public:
+ QtTestCustomObject(): QObject(), sum(0) {}
+
+public slots:
+ void sl1(MyType myType);
+
+signals:
+ void sig_custom(const CustomString &string);
+
+public:
+ int sum;
+};
+
+void QtTestCustomObject::sl1(MyType myType)
+{
+ sum = myType.i1 + myType.i2 + myType.i3;
+}
+
+void tst_QMetaObject::invokeCustomTypes()
+{
+ QtTestCustomObject obj;
+ MyType tp = {1, 1, 1};
+
+ QCOMPARE(obj.sum, 0);
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sl1", Q_ARG(MyType, tp)));
+ QCOMPARE(obj.sum, 3);
+}
+
+namespace NamespaceWithConstructibleClass
+{
+
+class ConstructibleClass : public QObject
+{
+ Q_OBJECT
+public:
+ Q_INVOKABLE ConstructibleClass(QObject *parent = 0)
+ : QObject(parent) {}
+};
+
+}
+
+void tst_QMetaObject::invokeMetaConstructor()
+{
+ const QMetaObject *mo = &QtTestObject::staticMetaObject;
+ {
+ QObject *obj = mo->newInstance();
+ QVERIFY(obj == 0);
+ }
+ {
+ QtTestObject obj;
+ QObject *obj2 = mo->newInstance(Q_ARG(QObject*, &obj));
+ QVERIFY(obj2 != 0);
+ QCOMPARE(obj2->parent(), (QObject*)&obj);
+ QVERIFY(qobject_cast<QtTestObject*>(obj2) != 0);
+ }
+ // class in namespace
+ const QMetaObject *nsmo = &NamespaceWithConstructibleClass::ConstructibleClass::staticMetaObject;
+ {
+ QtTestObject obj;
+ QObject *obj2 = nsmo->newInstance(Q_ARG(QObject*, &obj));
+ QVERIFY(obj2 != 0);
+ QCOMPARE(obj2->parent(), (QObject*)&obj);
+ QVERIFY(qobject_cast<NamespaceWithConstructibleClass::ConstructibleClass*>(obj2) != 0);
+ }
+}
+
+void tst_QMetaObject::invokeTypedefTypes()
+{
+ qRegisterMetaType<CustomString>("CustomString");
+ QtTestCustomObject obj;
+ QSignalSpy spy(&obj, SIGNAL(sig_custom(CustomString)));
+
+ QCOMPARE(spy.count(), 0);
+ CustomString arg("hello");
+ QVERIFY(QMetaObject::invokeMethod(&obj, "sig_custom", Q_ARG(CustomString, arg)));
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.at(0).count(), 1);
+ QCOMPARE(spy.at(0).at(0), QVariant(arg));
+}
+
+void tst_QMetaObject::normalizedSignature_data()
+{
+ QTest::addColumn<QString>("signature");
+ QTest::addColumn<QString>("result");
+
+ QTest::newRow("function") << "void foo()" << "void foo()";
+ QTest::newRow("spaces") << " void foo( ) " << "void foo()";
+ QTest::newRow("template args") << " void foo( QMap<a, a>, QList<b>) "
+ << "void foo(QMap<a,a>,QList<b>)";
+ QTest::newRow("rettype") << "QList<int, int> foo()" << "QList<int,int>foo()";
+ QTest::newRow("const rettype") << "const QString *foo()" << "const QString*foo()";
+ QTest::newRow("const ref") << "const QString &foo()" << "const QString&foo()";
+ QTest::newRow("reference") << "QString &foo()" << "QString&foo()";
+ QTest::newRow("const1") << "void foo(QString const *)" << "void foo(const QString*)";
+ QTest::newRow("const2") << "void foo(QString * const)" << "void foo(QString*const)";
+ QTest::newRow("const3") << "void foo(QString const &)" << "void foo(QString)";
+ QTest::newRow("const4") << "void foo(const int)" << "void foo(int)";
+ QTest::newRow("const5") << "void foo(const int, int const, const int &, int const &)"
+ << "void foo(int,int,int,int)";
+ QTest::newRow("const6") << "void foo(QList<const int>)" << "void foo(QList<const int>)";
+ QTest::newRow("const7") << "void foo(QList<const int*>)" << "void foo(QList<const int*>)";
+ QTest::newRow("const8") << "void foo(QList<int const*>)" << "void foo(QList<const int*>)";
+ QTest::newRow("const9") << "void foo(const Foo<Bar>)" << "void foo(Foo<Bar>)";
+ QTest::newRow("const10") << "void foo(Foo<Bar>const)" << "void foo(Foo<Bar>)";
+ QTest::newRow("const11") << "void foo(Foo<Bar> *const)" << "void foo(Foo<Bar>*const)";
+ QTest::newRow("const12") << "void foo(Foo<Bar>const*const *const)" << "void foo(Foo<Bar>*const*const)";
+ QTest::newRow("const13") << "void foo(const Foo<Bar>&)" << "void foo(Foo<Bar>)";
+ QTest::newRow("const14") << "void foo(Foo<Bar>const&)" << "void foo(Foo<Bar>)";
+
+ QTest::newRow("invalid1") << "a( b" << "a(b";
+}
+
+void tst_QMetaObject::normalizedSignature()
+{
+ QFETCH(QString, signature);
+ QFETCH(QString, result);
+
+ QCOMPARE(QMetaObject::normalizedSignature(signature.toLatin1()), result.toLatin1());
+}
+
+void tst_QMetaObject::normalizedType_data()
+{
+ QTest::addColumn<QString>("type");
+ QTest::addColumn<QString>("result");
+
+ QTest::newRow("simple") << "int" << "int";
+ QTest::newRow("white") << " int " << "int";
+ QTest::newRow("const1") << "int const *" << "const int*";
+ QTest::newRow("const2") << "const int *" << "const int*";
+ QTest::newRow("template1") << "QList<int const *>" << "QList<const int*>";
+ QTest::newRow("template2") << "QList<const int *>" << "QList<const int*>";
+ QTest::newRow("template3") << "QMap<QString, int>" << "QMap<QString,int>";
+ QTest::newRow("template4") << "const QMap<QString, int> &" << "QMap<QString,int>";
+ QTest::newRow("template5") << "QList< ::Foo::Bar>" << "QList< ::Foo::Bar>";
+ QTest::newRow("template6") << "QList<::Foo::Bar>" << "QList<::Foo::Bar>";
+ QTest::newRow("template7") << "QList<QList<int> >" << "QList<QList<int> >";
+ QTest::newRow("template8") << "QMap<const int, const short*>" << "QMap<const int,const short*>";
+ QTest::newRow("template9") << "QPair<const QPair<int, int const *> , QPair<QHash<int, const char*> > >" << "QPair<const QPair<int,const int*>,QPair<QHash<int,const char*> > >";
+ QTest::newRow("value1") << "const QString &" << "QString";
+ QTest::newRow("value2") << "QString const &" << "QString";
+ QTest::newRow("constInName1") << "constconst" << "constconst";
+ QTest::newRow("constInName2") << "constconst*" << "constconst*";
+ QTest::newRow("constInName3") << "const constconst&" << "constconst";
+ QTest::newRow("constInName4") << "constconst const*const" << "constconst*const";
+ QTest::newRow("class") << "const class foo&" << "foo";
+ QTest::newRow("struct") << "const struct foo*" << "const foo*";
+ QTest::newRow("struct2") << "struct foo const*" << "const foo*";
+ QTest::newRow("enum") << "enum foo" << "foo";
+}
+
+void tst_QMetaObject::normalizedType()
+{
+ QFETCH(QString, type);
+ QFETCH(QString, result);
+
+ QCOMPARE(QMetaObject::normalizedType(type.toLatin1()), result.toLatin1());
+}
+
+void tst_QMetaObject::customPropertyType()
+{
+ QMetaProperty prop = metaObject()->property(metaObject()->indexOfProperty("value3"));
+
+ QCOMPARE(prop.type(), QVariant::UserType);
+ QCOMPARE(prop.userType(), 0);
+
+ qRegisterMetaType<MyStruct>("MyStruct");
+ QCOMPARE(prop.userType(), QMetaType::type("MyStruct"));
+
+ prop = metaObject()->property(metaObject()->indexOfProperty("value4"));
+ QCOMPARE(prop.type(), QVariant::List);
+
+ prop = metaObject()->property(metaObject()->indexOfProperty("value5"));
+ QCOMPARE(prop.type(), QVariant::List);
+}
+
+void tst_QMetaObject::checkScope()
+{
+ MyNamespace::MyClass obj;
+
+ const QMetaObject *mo = obj.metaObject();
+ QMetaEnum me = mo->enumerator(mo->indexOfEnumerator("MyEnum"));
+ QVERIFY(me.isValid());
+ QVERIFY(!me.isFlag());
+ QCOMPARE(QLatin1String(me.scope()), QLatin1String("MyNamespace::MyClass"));
+ QCOMPARE(me.keyToValue("MyNamespace::MyClass::MyEnum2"), 1);
+ QCOMPARE(me.keyToValue("MyClass::MyEnum2"), -1);
+ QCOMPARE(me.keyToValue("MyNamespace::MyEnum2"), -1);
+ QCOMPARE(me.keyToValue("MyEnum2"), 1);
+ QCOMPARE(me.keyToValue("MyEnum"), -1);
+ QCOMPARE(QLatin1String(me.valueToKey(1)), QLatin1String("MyEnum2"));
+
+ QMetaEnum mf = mo->enumerator(mo->indexOfEnumerator("MyFlags"));
+ QVERIFY(mf.isValid());
+ QVERIFY(mf.isFlag());
+ QCOMPARE(QLatin1String(mf.scope()), QLatin1String("MyNamespace::MyClass"));
+ QCOMPARE(mf.keysToValue("MyNamespace::MyClass::MyFlag2"), 2);
+ QCOMPARE(mf.keysToValue("MyClass::MyFlag2"), -1);
+ QCOMPARE(mf.keysToValue("MyNamespace::MyFlag2"), -1);
+ QCOMPARE(mf.keysToValue("MyFlag2"), 2);
+ QCOMPARE(mf.keysToValue("MyFlag"), -1);
+ QCOMPARE(QLatin1String(mf.valueToKey(2)), QLatin1String("MyFlag2"));
+ QCOMPARE(mf.keysToValue("MyNamespace::MyClass::MyFlag1|MyNamespace::MyClass::MyFlag2"), 3);
+ QCOMPARE(mf.keysToValue("MyClass::MyFlag1|MyClass::MyFlag2"), -1);
+ QCOMPARE(mf.keysToValue("MyNamespace::MyFlag1|MyNamespace::MyFlag2"), -1);
+ QCOMPARE(mf.keysToValue("MyFlag1|MyFlag2"), 3);
+ QCOMPARE(mf.keysToValue("MyFlag2|MyFlag2"), 2);
+ QCOMPARE(mf.keysToValue("MyFlag1|MyNamespace::MyClass::MyFlag2"), 3);
+ QCOMPARE(mf.keysToValue("MyNamespace::MyClass::MyFlag2|MyNamespace::MyClass::MyFlag2"), 2);
+ QCOMPARE(QLatin1String(mf.valueToKeys(3)), QLatin1String("MyFlag1|MyFlag2"));
+}
+
+void tst_QMetaObject::propertyNotify()
+{
+ const QMetaObject *mo = metaObject();
+
+ QMetaProperty prop = mo->property(mo->indexOfProperty("value6"));
+ QVERIFY(prop.isValid());
+ QVERIFY(prop.hasNotifySignal());
+ QMetaMethod signal = prop.notifySignal();
+ QCOMPARE(signal.signature(), "value6Changed()");
+
+ prop = mo->property(mo->indexOfProperty("value7"));
+ QVERIFY(prop.isValid());
+ QVERIFY(prop.hasNotifySignal());
+ signal = prop.notifySignal();
+ QCOMPARE(signal.signature(), "value7Changed(QString)");
+
+ prop = mo->property(mo->indexOfProperty("value8"));
+ QVERIFY(prop.isValid());
+ QVERIFY(!prop.hasNotifySignal());
+ signal = prop.notifySignal();
+ QCOMPARE(signal.signature(), (const char *)0);
+
+ prop = mo->property(mo->indexOfProperty("value"));
+ QVERIFY(prop.isValid());
+ QVERIFY(!prop.hasNotifySignal());
+ signal = prop.notifySignal();
+ QCOMPARE(signal.signature(), (const char *)0);
+}
+
+void tst_QMetaObject::propertyConstant()
+{
+ const QMetaObject *mo = metaObject();
+
+ QMetaProperty prop = mo->property(mo->indexOfProperty("value8"));
+ QVERIFY(prop.isValid());
+ QVERIFY(!prop.isConstant());
+
+ prop = mo->property(mo->indexOfProperty("value9"));
+ QVERIFY(prop.isValid());
+ QVERIFY(prop.isConstant());
+}
+
+void tst_QMetaObject::propertyFinal()
+{
+ const QMetaObject *mo = metaObject();
+
+ QMetaProperty prop = mo->property(mo->indexOfProperty("value10"));
+ QVERIFY(prop.isValid());
+ QVERIFY(prop.isFinal());
+
+ prop = mo->property(mo->indexOfProperty("value9"));
+ QVERIFY(prop.isValid());
+ QVERIFY(!prop.isFinal());
+}
+
+class ClassInfoTestObjectA : public QObject
+{
+ Q_OBJECT
+ Q_CLASSINFO("Author", "Christopher Pike")
+};
+
+class ClassInfoTestObjectB : public ClassInfoTestObjectA
+{
+ Q_OBJECT
+};
+
+void tst_QMetaObject::classInfo()
+{
+ ClassInfoTestObjectB b;
+ int index = b.metaObject()->indexOfClassInfo("Author");
+ QCOMPARE(index, 0);
+ QVERIFY(index <= b.metaObject()->classInfoOffset());
+ QCOMPARE(QLatin1String(b.metaObject()->classInfo(index).value()), QLatin1String("Christopher Pike"));
+}
+
+void tst_QMetaObject::metaMethod()
+{
+ QString str("foo");
+ QString ret("bar");
+ QMetaMethod method;
+ QVERIFY(!method.invoke(this));
+ QVERIFY(!method.invoke(this, Q_ARG(QString, str)));
+ QVERIFY(!method.invoke(this, Q_RETURN_ARG(QString, ret), Q_ARG(QString, str)));
+ QCOMPARE(str, QString("foo"));
+ QCOMPARE(ret, QString("bar"));
+
+
+ QtTestObject obj;
+ QString t1("1"); QString t2("2"); QString t3("3"); QString t4("4"); QString t5("5");
+ QString t6("6"); QString t7("7"); QString t8("8"); QString t9("9"); QString t10("X");
+
+ int index = QtTestObject::staticMetaObject.indexOfMethod("sl5(QString,QString,QString,QString,QString)");
+ QVERIFY(index > 0);
+ method = QtTestObject::staticMetaObject.method(index);
+ //wrong args
+ QVERIFY(!method.invoke(&obj, Q_ARG(QString, "1"), Q_ARG(QString, "2"), Q_ARG(QString, "3"), Q_ARG(QString, "4")));
+ //QVERIFY(!method.invoke(&obj, Q_ARG(QString, "1"), Q_ARG(QString, "2"), Q_ARG(QString, "3"), Q_ARG(QString, "4"), Q_ARG(QString, "5"), Q_ARG(QString, "6")));
+ //QVERIFY(!method.invoke(&obj, Q_ARG(QString, "1"), Q_ARG(QString, "2"), Q_ARG(QString, "3"), Q_ARG(QString, "4"), Q_ARG(int, 5)));
+ QVERIFY(!method.invoke(&obj, Q_RETURN_ARG(QString, ret), Q_ARG(QString, "1"), Q_ARG(QString, "2"), Q_ARG(QString, "3"), Q_ARG(QString, "4"), Q_ARG(QString, "5")));
+
+ //wrong object
+ //QVERIFY(!method.invoke(this, Q_ARG(QString, "1"), Q_ARG(QString, "2"), Q_ARG(QString, "3"), Q_ARG(QString, "4"), Q_ARG(QString, "5")));
+ QVERIFY(!method.invoke(0, Q_ARG(QString, "1"), Q_ARG(QString, "2"), Q_ARG(QString, "3"), Q_ARG(QString, "4"), Q_ARG(QString, "5")));
+ QCOMPARE(ret, QString("bar"));
+ QCOMPARE(obj.slotResult, QString());
+
+ QVERIFY(method.invoke(&obj, Q_ARG(QString, "1"), Q_ARG(QString, "2"), Q_ARG(QString, "3"), Q_ARG(QString, "4"), Q_ARG(QString, "5")));
+ QCOMPARE(obj.slotResult, QString("sl5:12345"));
+
+ index = QtTestObject::staticMetaObject.indexOfMethod("sl13(QList<QString>)");
+ QVERIFY(index > 0);
+ QMetaMethod sl13 = QtTestObject::staticMetaObject.method(index);
+ QList<QString> returnValue, argument;
+ argument << QString("one") << QString("two") << QString("three");
+ //wrong object
+ //QVERIFY(!sl13.invoke(this, Q_RETURN_ARG(QList<QString>, returnValue), Q_ARG(QList<QString>, argument)));
+ QVERIFY(!sl13.invoke(0, Q_RETURN_ARG(QList<QString>, returnValue), Q_ARG(QList<QString>, argument)));
+ QCOMPARE(returnValue, QList<QString>());
+
+ QVERIFY(sl13.invoke(&obj, Q_RETURN_ARG(QList<QString>, returnValue), Q_ARG(QList<QString>, argument)));
+ QCOMPARE(returnValue, argument);
+ QCOMPARE(obj.slotResult, QString("sl13"));
+}
+
+void tst_QMetaObject::indexOfMethod_data()
+{
+ QTest::addColumn<QObject *>("object");
+ QTest::addColumn<QByteArray>("name");
+ QTest::addColumn<bool>("isSignal");
+ QTest::newRow("indexOfMethod_data") << (QObject*)this << QByteArray("indexOfMethod_data()") << false;
+ QTest::newRow("deleteLater") << (QObject*)this << QByteArray("deleteLater()") << false;
+ QTest::newRow("value6changed") << (QObject*)this << QByteArray("value6Changed()") << true;
+ QTest::newRow("value7changed") << (QObject*)this << QByteArray("value7Changed(QString)") << true;
+ QTest::newRow("destroyed") << (QObject*)this << QByteArray("destroyed()") << true;
+ QTest::newRow("destroyed2") << (QObject*)this << QByteArray("destroyed(QObject*)") << true;
+}
+
+void tst_QMetaObject::indexOfMethod()
+{
+ QFETCH(QObject *, object);
+ QFETCH(QByteArray, name);
+ QFETCH(bool, isSignal);
+ int idx = object->metaObject()->indexOfMethod(name);
+ QVERIFY(idx >= 0);
+ QCOMPARE(object->metaObject()->method(idx).signature(), name.constData());
+ QCOMPARE(object->metaObject()->indexOfSlot(name), isSignal ? -1 : idx);
+ QCOMPARE(object->metaObject()->indexOfSignal(name), !isSignal ? -1 : idx);
+}
+
+
+QTEST_MAIN(tst_QMetaObject)
+#include "tst_qmetaobject.moc"
diff --git a/tests/auto/corelib/kernel/qmetaproperty/qmetaproperty.pro b/tests/auto/corelib/kernel/qmetaproperty/qmetaproperty.pro
new file mode 100644
index 0000000000..4369f33b4c
--- /dev/null
+++ b/tests/auto/corelib/kernel/qmetaproperty/qmetaproperty.pro
@@ -0,0 +1,4 @@
+load(qttest_p4)
+QT = core gui
+SOURCES += tst_qmetaproperty.cpp
+
diff --git a/tests/auto/corelib/kernel/qmetaproperty/tst_qmetaproperty.cpp b/tests/auto/corelib/kernel/qmetaproperty/tst_qmetaproperty.cpp
new file mode 100644
index 0000000000..7a457b05fa
--- /dev/null
+++ b/tests/auto/corelib/kernel/qmetaproperty/tst_qmetaproperty.cpp
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** 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 <qobject.h>
+#include <qmetaobject.h>
+
+class tst_QMetaProperty : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(EnumType value WRITE setValue READ getValue)
+ Q_PROPERTY(EnumType value2 WRITE set_value READ get_value)
+ Q_PROPERTY(int value8 READ value8)
+ Q_PROPERTY(int value9 READ value9 CONSTANT)
+ Q_PROPERTY(int value10 READ value10 FINAL)
+
+private slots:
+ void hasStdCppSet();
+ void isConstant();
+ void isFinal();
+
+public:
+ enum EnumType { EnumType1 };
+
+ void setValue(EnumType) {}
+ EnumType getValue() const { return EnumType1; }
+ void set_value(EnumType) {}
+ EnumType get_value() const { return EnumType1; }
+
+ int value8() const { return 1; }
+ int value9() const { return 1; }
+ int value10() const { return 1; }
+};
+
+void tst_QMetaProperty::hasStdCppSet()
+{
+ const QMetaObject *mo = metaObject();
+
+ QMetaProperty prop = mo->property(mo->indexOfProperty("value"));
+ QVERIFY(prop.isValid());
+ QVERIFY(prop.hasStdCppSet());
+
+ prop = mo->property(mo->indexOfProperty("value2"));
+ QVERIFY(prop.isValid());
+ QVERIFY(!prop.hasStdCppSet());
+}
+
+void tst_QMetaProperty::isConstant()
+{
+ const QMetaObject *mo = metaObject();
+
+ QMetaProperty prop = mo->property(mo->indexOfProperty("value8"));
+ QVERIFY(prop.isValid());
+ QVERIFY(!prop.isConstant());
+
+ prop = mo->property(mo->indexOfProperty("value9"));
+ QVERIFY(prop.isValid());
+ QVERIFY(prop.isConstant());
+}
+
+void tst_QMetaProperty::isFinal()
+{
+ const QMetaObject *mo = metaObject();
+
+ QMetaProperty prop = mo->property(mo->indexOfProperty("value10"));
+ QVERIFY(prop.isValid());
+ QVERIFY(prop.isFinal());
+
+ prop = mo->property(mo->indexOfProperty("value9"));
+ QVERIFY(prop.isValid());
+ QVERIFY(!prop.isFinal());
+}
+
+QTEST_MAIN(tst_QMetaProperty)
+#include "tst_qmetaproperty.moc"
diff --git a/tests/auto/corelib/kernel/qmetatype/.gitignore b/tests/auto/corelib/kernel/qmetatype/.gitignore
new file mode 100644
index 0000000000..2d54331613
--- /dev/null
+++ b/tests/auto/corelib/kernel/qmetatype/.gitignore
@@ -0,0 +1 @@
+tst_qmetatype
diff --git a/tests/auto/corelib/kernel/qmetatype/qmetatype.pro b/tests/auto/corelib/kernel/qmetatype/qmetatype.pro
new file mode 100644
index 0000000000..ed1de83927
--- /dev/null
+++ b/tests/auto/corelib/kernel/qmetatype/qmetatype.pro
@@ -0,0 +1,4 @@
+load(qttest_p4)
+SOURCES += tst_qmetatype.cpp
+QT = core
+CONFIG += parallel_test
diff --git a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp
new file mode 100644
index 0000000000..3fb60acf57
--- /dev/null
+++ b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp
@@ -0,0 +1,365 @@
+/****************************************************************************
+**
+** 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 <QtCore>
+#include <QtTest/QtTest>
+
+#ifdef Q_OS_LINUX
+# include <pthread.h>
+#endif
+
+Q_DECLARE_METATYPE(QMetaType::Type)
+
+class tst_QMetaType: public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QList<QVariant> prop READ prop WRITE setProp)
+
+public:
+ tst_QMetaType() { propList << 42 << "Hello"; }
+
+ QList<QVariant> prop() const { return propList; }
+ void setProp(const QList<QVariant> &list) { propList = list; }
+
+private:
+ QList<QVariant> propList;
+
+private slots:
+ void defined();
+ void threadSafety();
+ void namespaces();
+ void qMetaTypeId();
+ void properties();
+ void normalizedTypes();
+ void typeName_data();
+ void typeName();
+ void construct();
+ void typedefs();
+ void isRegistered_data();
+ void isRegistered();
+ void unregisterType();
+ void QTBUG11316_registerStreamBuiltin();
+
+};
+
+struct Foo { int i; };
+
+void tst_QMetaType::defined()
+{
+ QCOMPARE(int(QMetaTypeId2<QString>::Defined), 1);
+ QCOMPARE(int(QMetaTypeId2<Foo>::Defined), 0);
+ QCOMPARE(int(QMetaTypeId2<void*>::Defined), 1);
+ QCOMPARE(int(QMetaTypeId2<int*>::Defined), 0);
+}
+
+struct Bar
+{
+ Bar()
+ {
+ // check re-entrancy
+ if (!QMetaType::isRegistered(qRegisterMetaType<Foo>("Foo"))) {
+ qWarning("%s: re-entrancy test failed", Q_FUNC_INFO);
+ ++failureCount;
+ }
+ }
+
+public:
+ static int failureCount;
+};
+
+int Bar::failureCount = 0;
+
+class MetaTypeTorturer: public QThread
+{
+ Q_OBJECT
+protected:
+ void run()
+ {
+ for (int i = 0; i < 1000; ++i) {
+ const QByteArray name = QString("Bar%1_%2").arg(i).arg((size_t)QThread::currentThreadId()).toLatin1();
+ const char *nm = name.constData();
+ int tp = qRegisterMetaType<Bar>(nm);
+#ifdef Q_OS_LINUX
+ pthread_yield();
+#endif
+ if (!QMetaType::isRegistered(tp)) {
+ ++failureCount;
+ qWarning() << name << "is not a registered metatype";
+ }
+ if (QMetaType::type(nm) != tp) {
+ ++failureCount;
+ qWarning() << "Wrong metatype returned for" << name;
+ }
+ if (QMetaType::typeName(tp) != name) {
+ ++failureCount;
+ qWarning() << "Wrong typeName returned for" << tp;
+ }
+ void *buf = QMetaType::construct(tp, 0);
+ void *buf2 = QMetaType::construct(tp, buf);
+ if (!buf) {
+ ++failureCount;
+ qWarning() << "Null buffer returned by QMetaType::construct(tp, 0)";
+ }
+ if (!buf2) {
+ ++failureCount;
+ qWarning() << "Null buffer returned by QMetaType::construct(tp, buf)";
+ }
+ QMetaType::destroy(tp, buf);
+ QMetaType::destroy(tp, buf2);
+ }
+ }
+public:
+ MetaTypeTorturer() : failureCount(0) { }
+ int failureCount;
+};
+
+void tst_QMetaType::threadSafety()
+{
+ MetaTypeTorturer t1;
+ MetaTypeTorturer t2;
+ MetaTypeTorturer t3;
+
+ t1.start();
+ t2.start();
+ t3.start();
+
+ QVERIFY(t1.wait());
+ QVERIFY(t2.wait());
+ QVERIFY(t3.wait());
+
+ QCOMPARE(t1.failureCount, 0);
+ QCOMPARE(t2.failureCount, 0);
+ QCOMPARE(t3.failureCount, 0);
+ QCOMPARE(Bar::failureCount, 0);
+}
+
+namespace TestSpace
+{
+ struct Foo { double d; };
+
+}
+Q_DECLARE_METATYPE(TestSpace::Foo)
+
+void tst_QMetaType::namespaces()
+{
+ TestSpace::Foo nf = { 11.12 };
+ QVariant v = qVariantFromValue(nf);
+ QCOMPARE(qvariant_cast<TestSpace::Foo>(v).d, 11.12);
+}
+
+void tst_QMetaType::qMetaTypeId()
+{
+ QCOMPARE(::qMetaTypeId<QString>(), int(QMetaType::QString));
+ QCOMPARE(::qMetaTypeId<int>(), int(QMetaType::Int));
+ QCOMPARE(::qMetaTypeId<TestSpace::Foo>(), QMetaType::type("TestSpace::Foo"));
+
+ QCOMPARE(::qMetaTypeId<char>(), QMetaType::type("char"));
+ QCOMPARE(::qMetaTypeId<uchar>(), QMetaType::type("unsigned char"));
+ QCOMPARE(::qMetaTypeId<signed char>(), QMetaType::type("signed char"));
+ QCOMPARE(::qMetaTypeId<qint8>(), QMetaType::type("qint8"));
+}
+
+void tst_QMetaType::properties()
+{
+ qRegisterMetaType<QList<QVariant> >("QList<QVariant>");
+
+ QVariant v = property("prop");
+
+ QCOMPARE(v.typeName(), "QVariantList");
+
+ QList<QVariant> values = v.toList();
+ QCOMPARE(values.count(), 2);
+ QCOMPARE(values.at(0).toInt(), 42);
+
+ values << 43 << "world";
+
+ QVERIFY(setProperty("prop", values));
+ v = property("prop");
+ QCOMPARE(v.toList().count(), 4);
+}
+
+template <typename T>
+struct Whity { T t; };
+
+Q_DECLARE_METATYPE( Whity < int > )
+Q_DECLARE_METATYPE(Whity<double>)
+
+void tst_QMetaType::normalizedTypes()
+{
+ int WhityIntId = ::qMetaTypeId<Whity<int> >();
+ int WhityDoubleId = ::qMetaTypeId<Whity<double> >();
+
+ QCOMPARE(QMetaType::type("Whity<int>"), WhityIntId);
+ QCOMPARE(QMetaType::type(" Whity < int > "), WhityIntId);
+ QCOMPARE(QMetaType::type("Whity<int >"), WhityIntId);
+
+ QCOMPARE(QMetaType::type("Whity<double>"), WhityDoubleId);
+ QCOMPARE(QMetaType::type(" Whity< double > "), WhityDoubleId);
+ QCOMPARE(QMetaType::type("Whity<double >"), WhityDoubleId);
+
+ QCOMPARE(qRegisterMetaType<Whity<int> >(" Whity < int > "), WhityIntId);
+ QCOMPARE(qRegisterMetaType<Whity<int> >("Whity<int>"), WhityIntId);
+ QCOMPARE(qRegisterMetaType<Whity<int> >("Whity<int > "), WhityIntId);
+
+ QCOMPARE(qRegisterMetaType<Whity<double> >(" Whity < double > "), WhityDoubleId);
+ QCOMPARE(qRegisterMetaType<Whity<double> >("Whity<double>"), WhityDoubleId);
+ QCOMPARE(qRegisterMetaType<Whity<double> >("Whity<double > "), WhityDoubleId);
+}
+
+void tst_QMetaType::typeName_data()
+{
+ QTest::addColumn<QMetaType::Type>("aType");
+ QTest::addColumn<QString>("aTypeName");
+
+ QTest::newRow("void") << QMetaType::Void << "void";
+ QTest::newRow("int") << QMetaType::Int << "int";
+ QTest::newRow("double") << QMetaType::Double << "double";
+ QTest::newRow("qlonglong") << QMetaType::LongLong << "qlonglong";
+ QTest::newRow("QRegExp") << QMetaType::QRegExp << "QRegExp";
+ QTest::newRow("void*") << QMetaType::VoidStar << "void*";
+ QTest::newRow("ulong") << QMetaType::ULong << "ulong";
+ QTest::newRow("QWidget*") << QMetaType::QWidgetStar << "QWidget*";
+}
+
+void tst_QMetaType::typeName()
+{
+ QFETCH(QMetaType::Type, aType);
+ QFETCH(QString, aTypeName);
+
+ QCOMPARE(QString::fromLatin1(QMetaType::typeName(aType)), aTypeName);
+}
+
+void tst_QMetaType::construct()
+{
+ QSize x(1, 1);
+ void *size = QMetaType::construct(QMetaType::QSize, &x);
+ QVERIFY(size);
+ QCOMPARE(static_cast<QSize *>(size)->width(), 1);
+ QMetaType::destroy(QMetaType::QSize, size);
+}
+
+typedef QString CustomString;
+Q_DECLARE_METATYPE(CustomString) //this line is useless
+
+void tst_QMetaType::typedefs()
+{
+ QCOMPARE(QMetaType::type("long long"), int(QMetaType::LongLong));
+ QCOMPARE(QMetaType::type("unsigned long long"), int(QMetaType::ULongLong));
+ QCOMPARE(QMetaType::type("qint8"), int(QMetaType::Char));
+ QCOMPARE(QMetaType::type("quint8"), int(QMetaType::UChar));
+ QCOMPARE(QMetaType::type("qint16"), int(QMetaType::Short));
+ QCOMPARE(QMetaType::type("quint16"), int(QMetaType::UShort));
+ QCOMPARE(QMetaType::type("qint32"), int(QMetaType::Int));
+ QCOMPARE(QMetaType::type("quint32"), int(QMetaType::UInt));
+ QCOMPARE(QMetaType::type("qint64"), int(QMetaType::LongLong));
+ QCOMPARE(QMetaType::type("quint64"), int(QMetaType::ULongLong));
+
+ // make sure the qreal typeId is the type id of the type it's defined to
+ QCOMPARE(QMetaType::type("qreal"), ::qMetaTypeId<qreal>());
+
+ qRegisterMetaType<CustomString>("CustomString");
+ QCOMPARE(QMetaType::type("CustomString"), ::qMetaTypeId<CustomString>());
+
+ typedef Whity<double> WhityDouble;
+ qRegisterMetaType<WhityDouble>("WhityDouble");
+ QCOMPARE(QMetaType::type("WhityDouble"), ::qMetaTypeId<WhityDouble>());
+}
+
+class IsRegisteredDummyType { };
+
+void tst_QMetaType::isRegistered_data()
+{
+ QTest::addColumn<int>("typeId");
+ QTest::addColumn<bool>("registered");
+
+ // predefined/custom types
+ QTest::newRow("QMetaType::Void") << int(QMetaType::Void) << true;
+ QTest::newRow("QMetaType::Int") << int(QMetaType::Int) << true;
+
+ int dummyTypeId = qRegisterMetaType<IsRegisteredDummyType>("IsRegisteredDummyType");
+
+ QTest::newRow("IsRegisteredDummyType") << dummyTypeId << true;
+
+ // unknown types
+ QTest::newRow("-1") << -1 << false;
+ QTest::newRow("-42") << -42 << false;
+ QTest::newRow("IsRegisteredDummyType + 1") << (dummyTypeId + 1) << false;
+}
+
+void tst_QMetaType::isRegistered()
+{
+ QFETCH(int, typeId);
+ QFETCH(bool, registered);
+ QCOMPARE(QMetaType::isRegistered(typeId), registered);
+}
+
+class RegUnreg
+{
+public:
+ RegUnreg() {};
+ RegUnreg(const RegUnreg &) {};
+ ~RegUnreg() {};
+};
+
+void tst_QMetaType::unregisterType()
+{
+ // cannot unregister standard types
+ int typeId = qRegisterMetaType<QList<QVariant> >("QList<QVariant>");
+ QCOMPARE(QMetaType::isRegistered(typeId), true);
+ QMetaType::unregisterType("QList<QVariant>");
+ QCOMPARE(QMetaType::isRegistered(typeId), true);
+ // allow unregister user types
+ typeId = qRegisterMetaType<RegUnreg>("RegUnreg");
+ QCOMPARE(QMetaType::isRegistered(typeId), true);
+ QMetaType::unregisterType("RegUnreg");
+ QCOMPARE(QMetaType::isRegistered(typeId), false);
+}
+
+void tst_QMetaType::QTBUG11316_registerStreamBuiltin()
+{
+ //should not crash;
+ qRegisterMetaTypeStreamOperators<QString>("QString");
+ qRegisterMetaTypeStreamOperators<QVariant>("QVariant");
+}
+
+QTEST_MAIN(tst_QMetaType)
+#include "tst_qmetatype.moc"
diff --git a/tests/auto/corelib/kernel/qmimedata/qmimedata.pro b/tests/auto/corelib/kernel/qmimedata/qmimedata.pro
new file mode 100644
index 0000000000..13fbe65edc
--- /dev/null
+++ b/tests/auto/corelib/kernel/qmimedata/qmimedata.pro
@@ -0,0 +1,4 @@
+load(qttest_p4)
+SOURCES += tst_qmimedata.cpp
+
+
diff --git a/tests/auto/corelib/kernel/qmimedata/tst_qmimedata.cpp b/tests/auto/corelib/kernel/qmimedata/tst_qmimedata.cpp
new file mode 100644
index 0000000000..6876862603
--- /dev/null
+++ b/tests/auto/corelib/kernel/qmimedata/tst_qmimedata.cpp
@@ -0,0 +1,343 @@
+/****************************************************************************
+**
+** 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 <QMimeData>
+
+class tst_QMimeData : public QObject
+{
+ Q_OBJECT
+public:
+ tst_QMimeData()
+ {
+ }
+
+private slots:
+ void clear() const;
+ void colorData() const;
+ void data() const;
+ void formats() const;
+ void hasColor() const;
+ void hasFormat() const;
+ void hasHtml() const;
+ void hasImage() const;
+ // hasText() covered by setText()
+ // hasUrls() covered by setUrls()
+ // html() covered by setHtml()
+ void imageData() const;
+ void removeFormat() const;
+ // setColorData() covered by hasColor()
+ // setData() covered in a few different tests
+ void setHtml() const;
+ // setImageData() covered in a few tests
+ void setText() const;
+ void setUrls() const;
+ // text() covered in setText()
+ // urls() covered by setUrls()
+};
+
+void tst_QMimeData::clear() const
+{
+ QMimeData mimeData;
+
+ // set, clear, verify empty
+ mimeData.setData("text/plain", "pirates");
+ QVERIFY(mimeData.hasText());
+ mimeData.clear();
+ QVERIFY(mimeData.hasText() == false);
+
+ // repopulate, verify not empty
+ mimeData.setData("text/plain", "pirates");
+ QVERIFY(mimeData.hasText());
+}
+
+void tst_QMimeData::colorData() const
+{
+ QMimeData mimeData;
+ QColor red = Qt::red;
+ QColor blue = Qt::blue;
+
+ // set, verify
+ mimeData.setColorData(red);
+ QVERIFY(mimeData.hasColor());
+ QCOMPARE(qvariant_cast<QColor>(mimeData.colorData()), red);
+
+ // change, verify
+ mimeData.setColorData(Qt::blue);
+ QVERIFY(mimeData.hasColor());
+ QCOMPARE(qvariant_cast<QColor>(mimeData.colorData()), blue);
+}
+
+void tst_QMimeData::data() const
+{
+ QMimeData mimeData;
+
+ // set text, verify
+ mimeData.setData("text/plain", "pirates");
+ QCOMPARE(mimeData.data("text/plain"), QByteArray("pirates"));
+ QVERIFY(mimeData.data("text/html").length() == 0);
+
+ // html time
+ mimeData.setData("text/html", "ninjas");
+ QCOMPARE(mimeData.data("text/html"), QByteArray("ninjas"));
+ QCOMPARE(mimeData.data("text/plain"), QByteArray("pirates")); // make sure text not damaged
+ QCOMPARE(mimeData.data("text/html"), mimeData.html().toLatin1());
+}
+
+void tst_QMimeData::formats() const
+{
+ QMimeData mimeData;
+
+ // set text, verify
+ mimeData.setData("text/plain", "pirates");
+ QCOMPARE(mimeData.formats(), QStringList() << "text/plain");
+
+ // set html, verify
+ mimeData.setData("text/html", "ninjas");
+ QCOMPARE(mimeData.formats(), QStringList() << "text/plain" << "text/html");
+
+ // clear, verify
+ mimeData.clear();
+ QCOMPARE(mimeData.formats(), QStringList());
+
+ // set an odd format, verify
+ mimeData.setData("foo/bar", "somevalue");
+ QCOMPARE(mimeData.formats(), QStringList() << "foo/bar");
+}
+
+void tst_QMimeData::hasColor() const
+{
+ QMimeData mimeData;
+
+ // initial state
+ QVERIFY(mimeData.hasColor() == false);
+
+ // set, verify
+ mimeData.setColorData(QColor(Qt::red));
+ QVERIFY(mimeData.hasColor());
+
+ // clear, verify
+ mimeData.clear();
+ QVERIFY(mimeData.hasColor() == false);
+
+ // set something else, verify
+ mimeData.setData("text/plain", "pirates");
+ QVERIFY(mimeData.hasColor() == false);
+}
+
+void tst_QMimeData::hasFormat() const
+{
+ QMimeData mimeData;
+
+ // initial state
+ QVERIFY(mimeData.hasFormat("text/plain") == false);
+
+ // add, verify
+ mimeData.setData("text/plain", "pirates");
+ QVERIFY(mimeData.hasFormat("text/plain"));
+ QVERIFY(mimeData.hasFormat("text/html") == false);
+
+ // clear, verify
+ mimeData.clear();
+ QVERIFY(mimeData.hasFormat("text/plain") == false);
+ QVERIFY(mimeData.hasFormat("text/html") == false);
+}
+
+void tst_QMimeData::hasHtml() const
+{
+ QMimeData mimeData;
+
+ // initial state
+ QVERIFY(mimeData.hasHtml() == false);
+
+ // add plain, verify false
+ mimeData.setData("text/plain", "pirates");
+ QVERIFY(mimeData.hasHtml() == false);
+
+ // add html, verify
+ mimeData.setData("text/html", "ninjas");
+ QVERIFY(mimeData.hasHtml());
+
+ // clear, verify
+ mimeData.clear();
+ QVERIFY(mimeData.hasHtml() == false);
+
+ // readd, verify
+ mimeData.setData("text/html", "ninjas");
+ QVERIFY(mimeData.hasHtml());
+}
+
+void tst_QMimeData::hasImage() const
+{
+ QMimeData mimeData;
+
+ // initial state
+ QVERIFY(mimeData.hasImage() == false);
+
+ // add text, verify false
+ mimeData.setData("text/plain", "pirates");
+ QVERIFY(mimeData.hasImage() == false);
+
+ // add image
+ mimeData.setImageData(QImage());
+ QVERIFY(mimeData.hasImage());
+
+ // clear, verify
+ mimeData.clear();
+ QVERIFY(mimeData.hasImage() == false);
+}
+
+void tst_QMimeData::imageData() const
+{
+ QMimeData mimeData;
+
+ // initial state
+ QCOMPARE(mimeData.imageData(), QVariant());
+
+ // set, test
+ mimeData.setImageData(QImage());
+ QVERIFY(mimeData.hasImage());
+ QCOMPARE(mimeData.imageData(), QVariant(QImage()));
+
+ // clear, verify
+ mimeData.clear();
+ QCOMPARE(mimeData.imageData(), QVariant());
+}
+
+void tst_QMimeData::removeFormat() const
+{
+ QMimeData mimeData;
+
+ // add, verify
+ mimeData.setData("text/plain", "pirates");
+ QVERIFY(mimeData.hasFormat("text/plain"));
+
+ // add another, verify
+ mimeData.setData("text/html", "ninjas");
+ QVERIFY(mimeData.hasFormat("text/html"));
+
+ // remove, verify
+ mimeData.removeFormat("text/plain");
+ QVERIFY(mimeData.hasFormat("text/plain") == false);
+ QVERIFY(mimeData.hasFormat("text/html"));
+
+ // remove, verify
+ mimeData.removeFormat("text/html");
+ QVERIFY(mimeData.hasFormat("text/plain") == false);
+ QVERIFY(mimeData.hasFormat("text/html") == false);
+}
+
+void tst_QMimeData::setHtml() const
+{
+ QMimeData mimeData;
+
+ // initial state
+ QVERIFY(mimeData.hasHtml() == false);
+
+ // add html, verify
+ mimeData.setHtml("ninjas");
+ QVERIFY(mimeData.hasHtml());
+ QCOMPARE(mimeData.html(), QLatin1String("ninjas"));
+
+ // reset html
+ mimeData.setHtml("pirates");
+ QVERIFY(mimeData.hasHtml());
+ QCOMPARE(mimeData.html(), QLatin1String("pirates"));
+}
+
+void tst_QMimeData::setText() const
+{
+ QMimeData mimeData;
+
+ // verify initial state
+ QCOMPARE(mimeData.text(), QLatin1String(""));
+ QVERIFY(mimeData.hasText() == false);
+
+ // set, verify
+ mimeData.setText("pirates");
+ QVERIFY(mimeData.hasText());
+ QCOMPARE(mimeData.text(), QLatin1String("pirates"));
+ QCOMPARE(mimeData.text().toLatin1(), mimeData.data("text/plain"));
+
+ // reset, verify
+ mimeData.setText("ninjas");
+ QVERIFY(mimeData.hasText());
+ QCOMPARE(mimeData.text(), QLatin1String("ninjas"));
+ QCOMPARE(mimeData.text().toLatin1(), mimeData.data("text/plain"));
+
+ // clear, verify
+ mimeData.clear();
+ QCOMPARE(mimeData.text(), QLatin1String(""));
+ QVERIFY(mimeData.hasText() == false);
+}
+
+void tst_QMimeData::setUrls() const
+{
+ QMimeData mimeData;
+ QList<QUrl> shortUrlList;
+ QList<QUrl> longUrlList;
+
+ // set up
+ shortUrlList += QUrl("http://qt.nokia.com");
+ longUrlList = shortUrlList;
+ longUrlList += QUrl("http://www.google.com");
+
+ // verify initial state
+ QVERIFY(mimeData.hasUrls() == false);
+
+ // set a few, verify
+ mimeData.setUrls(shortUrlList);
+ QCOMPARE(mimeData.urls(), shortUrlList);
+
+ // change them, verify
+ mimeData.setUrls(longUrlList);
+ QCOMPARE(mimeData.urls(), longUrlList);
+
+ // clear, verify
+ mimeData.clear();
+ QVERIFY(mimeData.hasUrls() == false);
+}
+
+
+QTEST_MAIN(tst_QMimeData)
+#include "tst_qmimedata.moc"
diff --git a/tests/auto/corelib/kernel/qobject/.gitignore b/tests/auto/corelib/kernel/qobject/.gitignore
new file mode 100644
index 0000000000..ea4d6d7f06
--- /dev/null
+++ b/tests/auto/corelib/kernel/qobject/.gitignore
@@ -0,0 +1,3 @@
+tst_qobject
+signalbug
+signalbug.exe
diff --git a/tests/auto/corelib/kernel/qobject/moc_oldnormalizeobject.cpp b/tests/auto/corelib/kernel/qobject/moc_oldnormalizeobject.cpp
new file mode 100644
index 0000000000..b275ff30fa
--- /dev/null
+++ b/tests/auto/corelib/kernel/qobject/moc_oldnormalizeobject.cpp
@@ -0,0 +1,154 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+/****************************************************************************
+** Meta object code from reading C++ file 'oldnormalizeobject.h'
+**
+** Created: Wed Nov 18 11:43:05 2009
+** by: The Qt Meta Object Compiler version 62 (Qt 4.6.0)
+**
+*****************************************************************************/
+
+// Yhis file was generated from moc version 4.6 to test binary compatibility
+// It should *not* be generated by the current moc
+
+#include "oldnormalizeobject.h"
+
+QT_BEGIN_MOC_NAMESPACE
+static const uint qt_meta_data_OldNormalizeObject[] = {
+
+ // content:
+ 4, // revision
+ 0, // classname
+ 0, 0, // classinfo
+ 6, 14, // methods
+ 0, 0, // properties
+ 0, 0, // enums/sets
+ 0, 0, // constructors
+ 0, // flags
+ 3, // signalCount
+
+ // signals: signature, parameters, type, tag, flags
+ 24, 20, 19, 19, 0x05,
+ 57, 20, 19, 19, 0x05,
+ 100, 20, 19, 19, 0x05,
+
+ // slots: signature, parameters, type, tag, flags
+ 149, 20, 19, 19, 0x0a,
+ 180, 20, 19, 19, 0x0a,
+ 221, 20, 19, 19, 0x0a,
+
+ 0 // eod
+};
+
+static const char qt_meta_stringdata_OldNormalizeObject[] = {
+ "OldNormalizeObject\0\0ref\0"
+ "typeRefSignal(Template<Class&>&)\0"
+ "constTypeRefSignal(Template<const Class&>)\0"
+ "typeConstRefSignal(Template<const Class&>const&)\0"
+ "typeRefSlot(Template<Class&>&)\0"
+ "constTypeRefSlot(Template<const Class&>)\0"
+ "typeConstRefSlot(Template<const Class&>const&)\0"
+};
+
+const QMetaObject OldNormalizeObject::staticMetaObject = {
+ { &QObject::staticMetaObject, qt_meta_stringdata_OldNormalizeObject,
+ qt_meta_data_OldNormalizeObject, 0 }
+};
+
+#ifdef Q_NO_DATA_RELOCATION
+const QMetaObject &OldNormalizeObject::getStaticMetaObject() { return staticMetaObject; }
+#endif //Q_NO_DATA_RELOCATION
+
+const QMetaObject *OldNormalizeObject::metaObject() const
+{
+ return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
+}
+
+void *OldNormalizeObject::qt_metacast(const char *_clname)
+{
+ if (!_clname) return 0;
+ if (!strcmp(_clname, qt_meta_stringdata_OldNormalizeObject))
+ return static_cast<void*>(const_cast< OldNormalizeObject*>(this));
+ return QObject::qt_metacast(_clname);
+}
+
+int OldNormalizeObject::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
+{
+ _id = QObject::qt_metacall(_c, _id, _a);
+ if (_id < 0)
+ return _id;
+ if (_c == QMetaObject::InvokeMetaMethod) {
+ switch (_id) {
+ case 0: typeRefSignal((*reinterpret_cast< Template<Class&>(*)>(_a[1]))); break;
+ case 1: constTypeRefSignal((*reinterpret_cast< const Template<const Class&>(*)>(_a[1]))); break;
+ case 2: typeConstRefSignal((*reinterpret_cast< Template<const Class&>const(*)>(_a[1]))); break;
+ case 3: typeRefSlot((*reinterpret_cast< Template<Class&>(*)>(_a[1]))); break;
+ case 4: constTypeRefSlot((*reinterpret_cast< const Template<const Class&>(*)>(_a[1]))); break;
+ case 5: typeConstRefSlot((*reinterpret_cast< Template<const Class&>const(*)>(_a[1]))); break;
+ default: ;
+ }
+ _id -= 6;
+ }
+ return _id;
+}
+
+// SIGNAL 0
+void OldNormalizeObject::typeRefSignal(Template<Class&> & _t1)
+{
+ void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
+ QMetaObject::activate(this, &staticMetaObject, 0, _a);
+}
+
+// SIGNAL 1
+void OldNormalizeObject::constTypeRefSignal(const Template<const Class&> & _t1)
+{
+ void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
+ QMetaObject::activate(this, &staticMetaObject, 1, _a);
+}
+
+// SIGNAL 2
+void OldNormalizeObject::typeConstRefSignal(Template<Class const&> const & _t1)
+{
+ void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
+ QMetaObject::activate(this, &staticMetaObject, 2, _a);
+}
+QT_END_MOC_NAMESPACE
diff --git a/tests/auto/corelib/kernel/qobject/oldnormalizeobject.h b/tests/auto/corelib/kernel/qobject/oldnormalizeobject.h
new file mode 100644
index 0000000000..4dfb7d97d2
--- /dev/null
+++ b/tests/auto/corelib/kernel/qobject/oldnormalizeobject.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** 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 QtTest module 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 OLDNORMALIZEOBJECT_H
+#define OLDNORMALIZEOBJECT_H
+
+#include <QObject>
+
+struct Struct;
+class Class;
+template <typename T> class Template;
+
+// An object with old moc output that incorrectly normalizes 'T<C> const &' in the function
+// signatures
+class OldNormalizeObject : public QObject
+{
+ /* tmake ignore Q_OBJECT */
+ Q_OBJECT
+
+signals:
+ void typeRefSignal(Template<Class &> &ref);
+ void constTypeRefSignal(const Template<const Class &> &ref);
+ void typeConstRefSignal(Template<Class const &> const &ref);
+
+public slots:
+ void typeRefSlot(Template<Class &> &) {}
+ void constTypeRefSlot(const Template<const Class &> &) {}
+ void typeConstRefSlot(Template<Class const &> const &) {}
+};
+
+#endif // OLDNORMALIZEOBJECT_H
diff --git a/tests/auto/corelib/kernel/qobject/qobject.pro b/tests/auto/corelib/kernel/qobject/qobject.pro
new file mode 100644
index 0000000000..113e14a61d
--- /dev/null
+++ b/tests/auto/corelib/kernel/qobject/qobject.pro
@@ -0,0 +1,3 @@
+TEMPLATE = subdirs
+SUBDIRS = tst_qobject.pro signalbug.pro
+CONFIG += parallel_test
diff --git a/tests/auto/corelib/kernel/qobject/signalbug.cpp b/tests/auto/corelib/kernel/qobject/signalbug.cpp
new file mode 100644
index 0000000000..f81de47e2d
--- /dev/null
+++ b/tests/auto/corelib/kernel/qobject/signalbug.cpp
@@ -0,0 +1,154 @@
+/****************************************************************************
+**
+** 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 "signalbug.h"
+
+#include <qcoreapplication.h>
+#include <qstring.h>
+
+#include <stdio.h>
+
+static int Step = 0;
+Sender RandomSender (0, 0);
+
+
+void TRACE (int step, const char *name)
+{
+ for (int t = 0; t < step - 1; t++)
+ fprintf (stderr, "\t");
+ fprintf (stderr, "Step %d: %s\n", step, name);
+ return;
+}
+
+
+Receiver::Receiver ()
+ : QObject ()
+{
+}
+
+void Receiver::received ()
+{
+ ::Step++;
+ const int stepCopy = ::Step;
+ TRACE (stepCopy, "Receiver::received()");
+ if (::Step != 2 && ::Step != 4)
+ qFatal("%s: Incorrect Step: %d (should be 2 or 4)", Q_FUNC_INFO, ::Step);
+
+ if (::Step == 2)
+ s->fire ();
+
+ fprintf (stderr, "Receiver<%s>::received() sender=%s\n",
+ (const char *) objectName ().toAscii (), sender ()->metaObject()->className());
+
+ TRACE (stepCopy, "ends Receiver::received()");
+}
+
+
+Disconnector::Disconnector ()
+ : QObject ()
+{
+}
+
+void Disconnector::received ()
+{
+ ::Step++;
+ const int stepCopy = ::Step;
+ TRACE (stepCopy, "Disconnector::received()");
+ if (::Step != 5 && ::Step != 6)
+ qFatal("%s: Incorrect Step: %d (should be 5 or 6)", Q_FUNC_INFO, ::Step);
+
+ fprintf (stderr, "Disconnector<%s>::received() sender=%s\n",
+ (const char *) objectName ().toAscii (), sender ()->metaObject()->className());
+ if (sender () == 0)
+ fprintf (stderr, "WE SHOULD NOT BE RECEIVING THIS SIGNAL\n");
+
+ if (::Step == 5)
+ {
+ disconnect (s, SIGNAL (fired ()), s->d, SLOT (received ()));
+
+ connect (&RandomSender, SIGNAL (fired ()), s->d, SLOT (received ()));
+ }
+
+ TRACE (stepCopy, "ends Disconnector::received()");
+}
+
+
+Sender::Sender (Receiver *r, Disconnector *d)
+ : QObject ()
+{
+ this->r = r; this->d = d;
+ if (r)
+ connect (this, SIGNAL (fired ()), r, SLOT (received ()));
+ if (d)
+ connect (this, SIGNAL (fired ()), d, SLOT (received ()));
+};
+
+void Sender::fire ()
+{
+ ::Step++;
+ const int stepCopy = ::Step;
+ TRACE (stepCopy, "Sender::fire()");
+ if (::Step != 1 && ::Step != 3)
+ qFatal("%s: Incorrect Step: %d (should be 1 or 3)", Q_FUNC_INFO, ::Step);
+
+ emit fired ();
+ TRACE (stepCopy, "ends Sender::fire()");
+}
+
+
+int main (int argc, char *argv [])
+{
+ QCoreApplication app (argc, argv);
+
+ Receiver r;
+ Disconnector d;
+ Sender s (&r, &d);
+
+ r.s = &s;
+ d.s = &s;
+
+
+ ::Step = 0;
+ s.fire ();
+ return 0;
+}
+
+
diff --git a/tests/auto/corelib/kernel/qobject/signalbug.h b/tests/auto/corelib/kernel/qobject/signalbug.h
new file mode 100644
index 0000000000..1d332239b3
--- /dev/null
+++ b/tests/auto/corelib/kernel/qobject/signalbug.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** 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 SIGNAL_BUG_H
+#define SIGNAL_BUG_H
+
+
+#include <QObject>
+
+
+class Sender;
+
+
+class Receiver : public QObject
+{
+Q_OBJECT
+
+public:
+ Receiver ();
+ virtual ~Receiver () {}
+
+protected slots:
+ void received ();
+
+public:
+ Sender *s;
+};
+
+
+class Disconnector : public QObject
+{
+Q_OBJECT
+
+public:
+ Disconnector ();
+ virtual ~Disconnector () {}
+
+protected slots:
+ void received ();
+
+public:
+ Sender *s;
+};
+
+
+class Sender : public QObject
+{
+Q_OBJECT
+
+public:
+ Sender (Receiver *r, Disconnector *d);
+ virtual ~Sender () {}
+
+ void fire ();
+
+signals:
+ void fired ();
+
+public:
+ Receiver *r;
+ Disconnector *d;
+};
+
+
+#endif // SIGNAL_BUG_H
diff --git a/tests/auto/corelib/kernel/qobject/signalbug.pro b/tests/auto/corelib/kernel/qobject/signalbug.pro
new file mode 100644
index 0000000000..a835264a93
--- /dev/null
+++ b/tests/auto/corelib/kernel/qobject/signalbug.pro
@@ -0,0 +1,19 @@
+######################################################################
+# Automatically generated by qmake (2.01a) Tue Aug 15 13:45:41 2006
+######################################################################
+
+TEMPLATE = app
+TARGET =
+DEPENDPATH += .
+INCLUDEPATH += .
+CONFIG -= app_bundle debug_and_release
+DESTDIR=.
+QT -= gui
+wince*: {
+ LIBS += coredll.lib
+}
+# Input
+HEADERS += signalbug.h
+SOURCES += signalbug.cpp
+
+
diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp
new file mode 100644
index 0000000000..faab1f5532
--- /dev/null
+++ b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp
@@ -0,0 +1,4065 @@
+/****************************************************************************
+**
+** 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 <qcoreapplication.h>
+
+#include <qpointer.h>
+#include <qtimer.h>
+#include <qregexp.h>
+#include <qmetaobject.h>
+#include <qvariant.h>
+
+#include <QTcpServer>
+#include <QTcpSocket>
+#include <QThread>
+#include <QMutex>
+#include <QWaitCondition>
+#include <QProcess>
+
+#include "qobject.h"
+#ifdef QT_BUILD_INTERNAL
+#include <private/qobject_p.h>
+#endif
+
+
+#include <math.h>
+
+//TESTED_CLASS=
+//TESTED_FILES=
+
+class tst_QObject : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QObject();
+ virtual ~tst_QObject();
+
+
+public slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+private slots:
+ void disconnect();
+ void connectByName();
+ void connectSignalsToSignalsWithDefaultArguments();
+ void receivers();
+ void normalize();
+ void qobject_castTemplate();
+ void findChildren();
+ void connectDisconnectNotify_data();
+ void connectDisconnectNotify();
+ void emitInDefinedOrder();
+ void customTypes();
+ void streamCustomTypes();
+ void metamethod();
+ void namespaces();
+ void threadSignalEmissionCrash();
+ void thread();
+ void thread0();
+ void moveToThread();
+ void sender();
+ void declareInterface();
+ void qpointerResetBeforeDestroyedSignal();
+ void testUserData();
+ void childDeletesItsSibling();
+ void dynamicProperties();
+ void floatProperty();
+ void qrealProperty();
+ void property();
+ void recursiveSignalEmission();
+ void blockingQueuedConnection();
+ void compatibilityChildInsertedEvents();
+ void installEventFilter();
+ void deleteSelfInSlot();
+ void disconnectSelfInSlotAndDeleteAfterEmit();
+ void dumpObjectInfo();
+ void connectToSender();
+ void qobjectConstCast();
+ void uniqConnection();
+ void interfaceIid();
+ void deleteQObjectWhenDeletingEvent();
+ void overloads();
+ void isSignalConnected();
+ void qMetaObjectConnect();
+ void qMetaObjectDisconnectOne();
+ void sameName();
+ void connectByMetaMethods();
+ void connectByMetaMethodSlotInsteadOfSignal();
+ void connectConstructorByMetaMethod();
+ void disconnectByMetaMethod();
+ void disconnectNotSignalMetaMethod();
+ void autoConnectionBehavior();
+ void baseDestroyed();
+protected:
+};
+
+tst_QObject::tst_QObject()
+{
+
+}
+
+tst_QObject::~tst_QObject()
+{
+
+}
+
+void tst_QObject::initTestCase()
+{
+}
+
+void tst_QObject::cleanupTestCase()
+{
+}
+
+void tst_QObject::init()
+{
+}
+
+void tst_QObject::cleanup()
+{
+}
+
+class SenderObject : public QObject
+{
+ Q_OBJECT
+
+public:
+ SenderObject() : aPublicSlotCalled(0), recursionCount(0) {}
+
+ void emitSignal1AfterRecursion()
+ {
+ if (recursionCount++ < 100)
+ emitSignal1AfterRecursion();
+ else
+ emitSignal1();
+ }
+
+ void emitSignal1() { emit signal1(); }
+ void emitSignal2() { emit signal2(); }
+ void emitSignal3() { emit signal3(); }
+ void emitSignal4() { emit signal4(); }
+
+signals:
+ void signal1();
+ void signal2();
+ void signal3();
+ void signal4();
+ QT_MOC_COMPAT void signal5();
+
+public slots:
+ void aPublicSlot() { aPublicSlotCalled++; }
+
+public:
+ Q_INVOKABLE void invoke1(){}
+ Q_SCRIPTABLE void sinvoke1(){}
+ int aPublicSlotCalled;
+protected:
+ Q_INVOKABLE QT_MOC_COMPAT void invoke2(){}
+ Q_INVOKABLE QT_MOC_COMPAT void invoke2(int){}
+ Q_SCRIPTABLE QT_MOC_COMPAT void sinvoke2(){}
+private:
+ Q_INVOKABLE void invoke3(int hinz = 0, int kunz = 0){Q_UNUSED(hinz) Q_UNUSED(kunz)}
+ Q_SCRIPTABLE void sinvoke3(){}
+
+ int recursionCount;
+};
+
+class ReceiverObject : public QObject
+{
+ Q_OBJECT
+
+public:
+ ReceiverObject() : sequence_slot1( 0 ),
+ sequence_slot2( 0 ),
+ sequence_slot3( 0 ),
+ sequence_slot4( 0 ) {}
+
+ void reset() {
+ sequence_slot4 = 0;
+ sequence_slot3 = 0;
+ sequence_slot2 = 0;
+ sequence_slot1 = 0;
+ count_slot1 = 0;
+ count_slot2 = 0;
+ count_slot3 = 0;
+ count_slot4 = 0;
+ }
+
+ int sequence_slot1;
+ int sequence_slot2;
+ int sequence_slot3;
+ int sequence_slot4;
+ int count_slot1;
+ int count_slot2;
+ int count_slot3;
+ int count_slot4;
+
+ bool called(int slot) {
+ switch (slot) {
+ case 1: return sequence_slot1;
+ case 2: return sequence_slot2;
+ case 3: return sequence_slot3;
+ case 4: return sequence_slot4;
+ default: return false;
+ }
+ }
+
+ static int sequence;
+
+public slots:
+ void slot1() { sequence_slot1 = ++sequence; count_slot1++; }
+ void slot2() { sequence_slot2 = ++sequence; count_slot2++; }
+ void slot3() { sequence_slot3 = ++sequence; count_slot3++; }
+ void slot4() { sequence_slot4 = ++sequence; count_slot4++; }
+
+};
+
+int ReceiverObject::sequence = 0;
+
+void tst_QObject::disconnect()
+{
+ SenderObject *s = new SenderObject;
+ ReceiverObject *r1 = new ReceiverObject;
+ ReceiverObject *r2 = new ReceiverObject;
+
+ connect( s, SIGNAL( signal1() ), r1, SLOT( slot1() ) );
+
+ connect( s, SIGNAL( signal2() ), r1, SLOT( slot2() ) );
+ connect( s, SIGNAL( signal3() ), r1, SLOT( slot3() ) );
+ connect( s, SIGNAL( signal4() ), r1, SLOT( slot4() ) );
+
+ s->emitSignal1();
+ s->emitSignal2();
+ s->emitSignal3();
+ s->emitSignal4();
+
+ QCOMPARE( r1->called(1), TRUE );
+ QCOMPARE( r1->called(2), TRUE );
+ QCOMPARE( r1->called(3), TRUE );
+ QCOMPARE( r1->called(4), TRUE );
+ r1->reset();
+
+ // usual disconnect with all parameters given
+ bool ret = QObject::disconnect( s, SIGNAL( signal1() ), r1, SLOT( slot1() ) );
+
+ s->emitSignal1();
+
+ QCOMPARE( r1->called(1), FALSE );
+ r1->reset();
+
+ QCOMPARE( ret, TRUE );
+ ret = QObject::disconnect( s, SIGNAL( signal1() ), r1, SLOT( slot1() ) );
+ QCOMPARE( ret, FALSE );
+
+ // disconnect all signals from s from all slots from r1
+ QObject::disconnect( s, 0, r1, 0 );
+
+ s->emitSignal2();
+ s->emitSignal3();
+ s->emitSignal4();
+
+ QCOMPARE( r1->called(2), FALSE );
+ QCOMPARE( r1->called(3), FALSE );
+ QCOMPARE( r1->called(4), FALSE );
+ r1->reset();
+
+ connect( s, SIGNAL( signal1() ), r1, SLOT( slot1() ) );
+ connect( s, SIGNAL( signal1() ), r1, SLOT( slot2() ) );
+ connect( s, SIGNAL( signal1() ), r1, SLOT( slot3() ) );
+ connect( s, SIGNAL( signal2() ), r1, SLOT( slot4() ) );
+
+ // disconnect s's signal1() from all slots of r1
+ QObject::disconnect( s, SIGNAL( signal1() ), r1, 0 );
+
+ s->emitSignal1();
+ s->emitSignal2();
+
+ QCOMPARE( r1->called(1), FALSE );
+ QCOMPARE( r1->called(2), FALSE );
+ QCOMPARE( r1->called(3), FALSE );
+ QCOMPARE( r1->called(4), TRUE );
+ r1->reset();
+ // make sure all is disconnected again
+ QObject::disconnect( s, 0, r1, 0 );
+
+ connect( s, SIGNAL( signal1() ), r1, SLOT( slot1() ) );
+ connect( s, SIGNAL( signal1() ), r2, SLOT( slot1() ) );
+ connect( s, SIGNAL( signal2() ), r1, SLOT( slot2() ) );
+ connect( s, SIGNAL( signal2() ), r2, SLOT( slot2() ) );
+ connect( s, SIGNAL( signal3() ), r1, SLOT( slot3() ) );
+ connect( s, SIGNAL( signal3() ), r2, SLOT( slot3() ) );
+
+ // disconnect signal1() from all receivers
+ QObject::disconnect( s, SIGNAL( signal1() ), 0, 0 );
+ s->emitSignal1();
+ s->emitSignal2();
+ s->emitSignal3();
+
+ QCOMPARE( r1->called(1), FALSE );
+ QCOMPARE( r2->called(1), FALSE );
+ QCOMPARE( r1->called(2), TRUE );
+ QCOMPARE( r2->called(2), TRUE );
+ QCOMPARE( r1->called(2), TRUE );
+ QCOMPARE( r2->called(2), TRUE );
+
+ r1->reset();
+ r2->reset();
+
+ // disconnect all signals of s from all receivers
+ QObject::disconnect( s, 0, 0, 0 );
+
+ QCOMPARE( r1->called(2), FALSE );
+ QCOMPARE( r2->called(2), FALSE );
+ QCOMPARE( r1->called(2), FALSE );
+ QCOMPARE( r2->called(2), FALSE );
+
+ delete r2;
+ delete r1;
+ delete s;
+}
+
+class AutoConnectSender : public QObject
+{
+ Q_OBJECT
+
+public:
+ AutoConnectSender(QObject *parent)
+ : QObject(parent)
+ {}
+
+ void emitSignalNoParams() { emit signalNoParams(); }
+ void emitSignalWithParams(int i) { emit signalWithParams(i); }
+ void emitSignalWithParams(int i, QString string) { emit signalWithParams(i, string); }
+ void emitSignalManyParams(int i1, int i2, int i3, QString string, bool onoff) { emit signalManyParams(i1, i2, i3, string, onoff); }
+ void emitSignalManyParams2(int i1, int i2, int i3, QString string, bool onoff) { emit signalManyParams2(i1, i2, i3, string, onoff); }
+ void emitSignalLoopBack() { emit signalLoopBack(); }
+
+signals:
+ void signalNoParams();
+ void signalWithParams(int i);
+ void signalWithParams(int i, QString string);
+ void signalManyParams(int i1, int i2, int i3, QString string, bool onoff);
+ void signalManyParams(int i1, int i2, int i3, QString string, bool onoff, bool);
+ void signalManyParams2(int i1, int i2, int i3, QString string, bool onoff);
+ void signalLoopBack();
+};
+
+class AutoConnectReceiver : public QObject
+{
+ Q_OBJECT
+
+public:
+ AutoConnectReceiver()
+ {
+ reset();
+
+ connect(this, SIGNAL(on_Sender_signalLoopBack()), this, SLOT(slotLoopBack()));
+ }
+
+ void reset() {
+ called_slot10 = 0;
+ called_slot9 = 0;
+ called_slot8 = 0;
+ called_slot7 = 0;
+ called_slot6 = 0;
+ called_slot5 = 0;
+ called_slot4 = 0;
+ called_slot3 = 0;
+ called_slot2 = 0;
+ called_slot1 = 0;
+ }
+
+ int called_slot1;
+ int called_slot2;
+ int called_slot3;
+ int called_slot4;
+ int called_slot5;
+ int called_slot6;
+ int called_slot7;
+ int called_slot8;
+ int called_slot9;
+ int called_slot10;
+
+ bool called(int slot) {
+ switch (slot) {
+ case 1: return called_slot1;
+ case 2: return called_slot2;
+ case 3: return called_slot3;
+ case 4: return called_slot4;
+ case 5: return called_slot5;
+ case 6: return called_slot6;
+ case 7: return called_slot7;
+ case 8: return called_slot8;
+ case 9: return called_slot9;
+ case 10: return called_slot10;
+ default: return false;
+ }
+ }
+
+public slots:
+ void on_Sender_signalNoParams() { ++called_slot1; }
+ void on_Sender_signalWithParams(int i = 0) { ++called_slot2; Q_UNUSED(i); }
+ void on_Sender_signalWithParams(int i, QString string) { ++called_slot3; Q_UNUSED(i);Q_UNUSED(string); }
+ void on_Sender_signalManyParams() { ++called_slot4; }
+ void on_Sender_signalManyParams(int i1, int i2, int i3, QString string, bool onoff) { ++called_slot5; Q_UNUSED(i1);Q_UNUSED(i2);Q_UNUSED(i3);Q_UNUSED(string);Q_UNUSED(onoff); }
+ void on_Sender_signalManyParams(int i1, int i2, int i3, QString string, bool onoff, bool dummy)
+ { ++called_slot6; Q_UNUSED(i1);Q_UNUSED(i2);Q_UNUSED(i3);Q_UNUSED(string);Q_UNUSED(onoff); Q_UNUSED(dummy);}
+ void on_Sender_signalManyParams2(int i1, int i2, int i3, QString string, bool onoff)
+ { ++called_slot7; Q_UNUSED(i1);Q_UNUSED(i2);Q_UNUSED(i3);Q_UNUSED(string);Q_UNUSED(onoff); }
+ void slotLoopBack() { ++called_slot8; }
+
+protected slots:
+ void o() { ++called_slot9; }
+ void on() { ++called_slot10; }
+
+signals:
+ void on_Sender_signalLoopBack();
+};
+
+void tst_QObject::connectByName()
+{
+ AutoConnectReceiver receiver;
+ AutoConnectSender sender(&receiver);
+ sender.setObjectName("Sender");
+
+ QMetaObject::connectSlotsByName(&receiver);
+
+ sender.emitSignalNoParams();
+ QCOMPARE(receiver.called(1), true);
+ QCOMPARE(receiver.called(2), false);
+ QCOMPARE(receiver.called(3), false);
+ QCOMPARE(receiver.called(4), false);
+ QCOMPARE(receiver.called(5), false);
+ QCOMPARE(receiver.called(6), false);
+ QCOMPARE(receiver.called(7), false);
+ QCOMPARE(receiver.called(8), false);
+ QCOMPARE(receiver.called(9), false);
+ QCOMPARE(receiver.called(10), false);
+ receiver.reset();
+
+ sender.emitSignalWithParams(0);
+ QCOMPARE(receiver.called(1), false);
+ QCOMPARE(receiver.called(2), true);
+ QCOMPARE(receiver.called(3), false);
+ QCOMPARE(receiver.called(4), false);
+ QCOMPARE(receiver.called(5), false);
+ QCOMPARE(receiver.called(6), false);
+ QCOMPARE(receiver.called(7), false);
+ QCOMPARE(receiver.called(8), false);
+ QCOMPARE(receiver.called(9), false);
+ QCOMPARE(receiver.called(10), false);
+ receiver.reset();
+
+ sender.emitSignalWithParams(0, "string");
+ QCOMPARE(receiver.called(1), false);
+ QCOMPARE(receiver.called(2), false);
+ QCOMPARE(receiver.called(3), true);
+ QCOMPARE(receiver.called(4), false);
+ QCOMPARE(receiver.called(5), false);
+ QCOMPARE(receiver.called(6), false);
+ QCOMPARE(receiver.called(7), false);
+ QCOMPARE(receiver.called(8), false);
+ QCOMPARE(receiver.called(9), false);
+ QCOMPARE(receiver.called(10), false);
+ receiver.reset();
+
+ sender.emitSignalManyParams(1, 2, 3, "string", true);
+ QCOMPARE(receiver.called(1), false);
+ QCOMPARE(receiver.called(2), false);
+ QCOMPARE(receiver.called(3), false);
+ QCOMPARE(receiver.called(4), true);
+ QCOMPARE(receiver.called(5), true);
+ QCOMPARE(receiver.called(6), false);
+ QCOMPARE(receiver.called(7), false);
+ QCOMPARE(receiver.called(8), false);
+ QCOMPARE(receiver.called(9), false);
+ QCOMPARE(receiver.called(10), false);
+ receiver.reset();
+
+ sender.emitSignalManyParams2(1, 2, 3, "string", true);
+ QCOMPARE(receiver.called(1), false);
+ QCOMPARE(receiver.called(2), false);
+ QCOMPARE(receiver.called(3), false);
+ QCOMPARE(receiver.called(4), false);
+ QCOMPARE(receiver.called(5), false);
+ QCOMPARE(receiver.called(6), false);
+ QCOMPARE(receiver.called(7), true);
+ QCOMPARE(receiver.called(8), false);
+ QCOMPARE(receiver.called(9), false);
+ QCOMPARE(receiver.called(10), false);
+ receiver.reset();
+
+ sender.emitSignalLoopBack();
+ QCOMPARE(receiver.called(1), false);
+ QCOMPARE(receiver.called(2), false);
+ QCOMPARE(receiver.called(3), false);
+ QCOMPARE(receiver.called(4), false);
+ QCOMPARE(receiver.called(5), false);
+ QCOMPARE(receiver.called(6), false);
+ QCOMPARE(receiver.called(7), false);
+ QCOMPARE(receiver.called(8), true);
+ QCOMPARE(receiver.called(9), false);
+ QCOMPARE(receiver.called(10), false);
+ receiver.reset();
+}
+
+void tst_QObject::qobject_castTemplate()
+{
+ QObject *o = 0;
+ QVERIFY( !::qobject_cast<QObject*>(o) );
+
+ o = new SenderObject;
+ QVERIFY( ::qobject_cast<SenderObject*>(o) );
+ QVERIFY( ::qobject_cast<QObject*>(o) );
+ QVERIFY( !::qobject_cast<ReceiverObject*>(o) );
+ delete o;
+}
+
+void tst_QObject::findChildren()
+{
+ QObject o;
+ QObject o1(&o);
+ QObject o2(&o);
+ QObject o11(&o1);
+ QObject o12(&o1);
+ QObject o111(&o11);
+ QObject unnamed(&o);
+ QTimer t1(&o);
+ QTimer t121(&o12);
+ QTimer emptyname(&o);
+
+ o.setObjectName("o");
+ o1.setObjectName("o1");
+ o2.setObjectName("o2");
+ o11.setObjectName("o11");
+ o12.setObjectName("o12");
+ o111.setObjectName("o111");
+ t1.setObjectName("t1");
+ t121.setObjectName("t121");
+ emptyname.setObjectName("");
+
+ QObject *op = 0;
+
+ op = qFindChild<QObject*>(&o, "o1");
+ QCOMPARE(op, &o1);
+ op = qFindChild<QObject*>(&o, "o2");
+ QCOMPARE(op, &o2);
+ op = qFindChild<QObject*>(&o, "o11");
+ QCOMPARE(op, &o11);
+ op = qFindChild<QObject*>(&o, "o12");
+ QCOMPARE(op, &o12);
+ op = qFindChild<QObject*>(&o, "o111");
+ QCOMPARE(op, &o111);
+ op = qFindChild<QObject*>(&o, "t1");
+ QCOMPARE(op, static_cast<QObject *>(&t1));
+ op = qFindChild<QObject*>(&o, "t121");
+ QCOMPARE(op, static_cast<QObject *>(&t121));
+ op = qFindChild<QTimer*>(&o, "t1");
+ QCOMPARE(op, static_cast<QObject *>(&t1));
+ op = qFindChild<QTimer*>(&o, "t121");
+ QCOMPARE(op, static_cast<QObject *>(&t121));
+ op = qFindChild<QTimer*>(&o, "o12");
+ QCOMPARE(op, static_cast<QObject *>(0));
+ op = qFindChild<QObject*>(&o, "o");
+ QCOMPARE(op, static_cast<QObject *>(0));
+ op = qFindChild<QObject*>(&o, "harry");
+ QCOMPARE(op, static_cast<QObject *>(0));
+ op = qFindChild<QObject*>(&o, "o1");
+ QCOMPARE(op, &o1);
+
+ QList<QObject*> l;
+ QList<QTimer*> tl;
+
+ l = qFindChildren<QObject*>(&o, "o1");
+ QCOMPARE(l.size(), 1);
+ QCOMPARE(l.at(0), &o1);
+ l = qFindChildren<QObject*>(&o, "o2");
+ QCOMPARE(l.size(), 1);
+ QCOMPARE(l.at(0), &o2);
+ l = qFindChildren<QObject*>(&o, "o11");
+ QCOMPARE(l.size(), 1);
+ QCOMPARE(l.at(0), &o11);
+ l = qFindChildren<QObject*>(&o, "o12");
+ QCOMPARE(l.size(), 1);
+ QCOMPARE(l.at(0), &o12);
+ l = qFindChildren<QObject*>(&o, "o111");
+ QCOMPARE(l.size(), 1);
+ QCOMPARE(l.at(0), &o111);
+ l = qFindChildren<QObject*>(&o, "t1");
+ QCOMPARE(l.size(), 1);
+ QCOMPARE(l.at(0), static_cast<QObject *>(&t1));
+ l = qFindChildren<QObject*>(&o, "t121");
+ QCOMPARE(l.size(), 1);
+ QCOMPARE(l.at(0), static_cast<QObject *>(&t121));
+ tl = qFindChildren<QTimer*>(&o, "t1");
+ QCOMPARE(tl.size(), 1);
+ QCOMPARE(tl.at(0), &t1);
+ tl = qFindChildren<QTimer*>(&o, "t121");
+ QCOMPARE(tl.size(), 1);
+ QCOMPARE(tl.at(0), &t121);
+ l = qFindChildren<QObject*>(&o, "o");
+ QCOMPARE(l.size(), 0);
+ l = qFindChildren<QObject*>(&o, "harry");
+ QCOMPARE(l.size(), 0);
+ tl = qFindChildren<QTimer*>(&o, "o12");
+ QCOMPARE(tl.size(), 0);
+ l = qFindChildren<QObject*>(&o, "o1");
+ QCOMPARE(l.size(), 1);
+ QCOMPARE(l.at(0), &o1);
+
+ l = qFindChildren<QObject*>(&o, QRegExp("o.*"));
+ QCOMPARE(l.size(), 5);
+ QVERIFY(l.contains(&o1));
+ QVERIFY(l.contains(&o2));
+ QVERIFY(l.contains(&o11));
+ QVERIFY(l.contains(&o12));
+ QVERIFY(l.contains(&o111));
+ l = qFindChildren<QObject*>(&o, QRegExp("t.*"));
+ QCOMPARE(l.size(), 2);
+ QVERIFY(l.contains(&t1));
+ QVERIFY(l.contains(&t121));
+ tl = qFindChildren<QTimer*>(&o, QRegExp(".*"));
+ QCOMPARE(tl.size(), 3);
+ QVERIFY(tl.contains(&t1));
+ QVERIFY(tl.contains(&t121));
+ tl = qFindChildren<QTimer*>(&o, QRegExp("o.*"));
+ QCOMPARE(tl.size(), 0);
+ l = qFindChildren<QObject*>(&o, QRegExp("harry"));
+ QCOMPARE(l.size(), 0);
+
+ // empty and null string check
+ op = qFindChild<QObject*>(&o);
+ QCOMPARE(op, &o1);
+ op = qFindChild<QObject*>(&o, "");
+ QCOMPARE(op, &unnamed);
+ op = qFindChild<QObject*>(&o, "unnamed");
+ QCOMPARE(op, static_cast<QObject *>(0));
+
+ l = qFindChildren<QObject*>(&o);
+ QCOMPARE(l.size(), 9);
+ l = qFindChildren<QObject*>(&o, "");
+ QCOMPARE(l.size(), 2);
+ l = qFindChildren<QObject*>(&o, "unnamed");
+ QCOMPARE(l.size(), 0);
+
+ tl = o.findChildren<QTimer *>("t1");
+ QCOMPARE(tl.size(), 1);
+ QCOMPARE(tl.at(0), &t1);
+
+ // Find direct child/children
+
+ op = o.findChild<QObject*>("o1", Qt::FindDirectChildrenOnly);
+ QCOMPARE(op, &o1);
+ op = o.findChild<QObject*>("o2", Qt::FindDirectChildrenOnly);
+ QCOMPARE(op, &o2);
+ op = o.findChild<QObject*>("o11", Qt::FindDirectChildrenOnly);
+ QCOMPARE(op, static_cast<QObject *>(0));
+ op = o.findChild<QObject*>("o12", Qt::FindDirectChildrenOnly);
+ QCOMPARE(op, static_cast<QObject *>(0));
+ op = o.findChild<QObject*>("o111", Qt::FindDirectChildrenOnly);
+ QCOMPARE(op, static_cast<QObject *>(0));
+ op = o.findChild<QObject*>("t1", Qt::FindDirectChildrenOnly);
+ QCOMPARE(op, static_cast<QObject *>(&t1));
+ op = o.findChild<QObject*>("t121", Qt::FindDirectChildrenOnly);
+ QCOMPARE(op, static_cast<QObject *>(0));
+ op = o.findChild<QTimer*>("t1", Qt::FindDirectChildrenOnly);
+ QCOMPARE(op, static_cast<QObject *>(&t1));
+ op = o.findChild<QTimer*>("t121", Qt::FindDirectChildrenOnly);
+ QCOMPARE(op, static_cast<QObject *>(0));
+ op = o.findChild<QTimer*>("o12", Qt::FindDirectChildrenOnly);
+ QCOMPARE(op, static_cast<QObject *>(0));
+ op = o.findChild<QObject*>("o", Qt::FindDirectChildrenOnly);
+ QCOMPARE(op, static_cast<QObject *>(0));
+ op = o.findChild<QObject*>("harry", Qt::FindDirectChildrenOnly);
+ QCOMPARE(op, static_cast<QObject *>(0));
+ op = o.findChild<QObject*>("o1", Qt::FindDirectChildrenOnly);
+ QCOMPARE(op, &o1);
+
+ l = o.findChildren<QObject*>("o1", Qt::FindDirectChildrenOnly);
+ QCOMPARE(l.size(), 1);
+ QCOMPARE(l.at(0), &o1);
+ l = o.findChildren<QObject*>("o2", Qt::FindDirectChildrenOnly);
+ QCOMPARE(l.size(), 1);
+ QCOMPARE(l.at(0), &o2);
+ l = o.findChildren<QObject*>("o11", Qt::FindDirectChildrenOnly);
+ QCOMPARE(l.size(), 0);
+ l = o.findChildren<QObject*>("o12", Qt::FindDirectChildrenOnly);
+ QCOMPARE(l.size(), 0);
+ l = o.findChildren<QObject*>("o111", Qt::FindDirectChildrenOnly);
+ QCOMPARE(l.size(), 0);
+ l = o.findChildren<QObject*>("t1", Qt::FindDirectChildrenOnly);
+ QCOMPARE(l.size(), 1);
+ QCOMPARE(l.at(0), static_cast<QObject *>(&t1));
+ l = o.findChildren<QObject*>("t121", Qt::FindDirectChildrenOnly);
+ QCOMPARE(l.size(), 0);
+ tl = o.findChildren<QTimer*>("t1", Qt::FindDirectChildrenOnly);
+ QCOMPARE(tl.size(), 1);
+ QCOMPARE(tl.at(0), &t1);
+ tl = o.findChildren<QTimer*>("t121", Qt::FindDirectChildrenOnly);
+ QCOMPARE(tl.size(), 0);
+ l = o.findChildren<QObject*>("o", Qt::FindDirectChildrenOnly);
+ QCOMPARE(l.size(), 0);
+ l = o.findChildren<QObject*>("harry", Qt::FindDirectChildrenOnly);
+ QCOMPARE(l.size(), 0);
+ tl = o.findChildren<QTimer*>("o12", Qt::FindDirectChildrenOnly);
+ QCOMPARE(tl.size(), 0);
+ l = o.findChildren<QObject*>("o1", Qt::FindDirectChildrenOnly);
+ QCOMPARE(l.size(), 1);
+ QCOMPARE(l.at(0), &o1);
+
+ l = o.findChildren<QObject*>(QRegExp("o.*"), Qt::FindDirectChildrenOnly);
+ QCOMPARE(l.size(), 2);
+ QVERIFY(l.contains(&o1));
+ QVERIFY(l.contains(&o2));
+ l = o.findChildren<QObject*>(QRegExp("t.*"), Qt::FindDirectChildrenOnly);
+ QCOMPARE(l.size(), 1);
+ QVERIFY(l.contains(&t1));
+ tl = o.findChildren<QTimer*>(QRegExp(".*"), Qt::FindDirectChildrenOnly);
+ QCOMPARE(tl.size(), 2);
+ QVERIFY(tl.contains(&t1));
+ tl = o.findChildren<QTimer*>(QRegExp("o.*"), Qt::FindDirectChildrenOnly);
+ QCOMPARE(tl.size(), 0);
+ l = o.findChildren<QObject*>(QRegExp("harry"), Qt::FindDirectChildrenOnly);
+ QCOMPARE(l.size(), 0);
+
+ // empty and null string check
+ op = o.findChild<QObject*>(QString(), Qt::FindDirectChildrenOnly);
+ QCOMPARE(op, &o1);
+ op = o.findChild<QObject*>("", Qt::FindDirectChildrenOnly);
+ QCOMPARE(op, &unnamed);
+ op = o.findChild<QObject*>("unnamed", Qt::FindDirectChildrenOnly);
+ QCOMPARE(op, static_cast<QObject *>(0));
+
+ l = o.findChildren<QObject*>(QString(), Qt::FindDirectChildrenOnly);
+ QCOMPARE(l.size(), 5);
+ l = o.findChildren<QObject*>("", Qt::FindDirectChildrenOnly);
+ QCOMPARE(l.size(), 2);
+ l = o.findChildren<QObject*>("unnamed", Qt::FindDirectChildrenOnly);
+ QCOMPARE(l.size(), 0);
+
+ tl = o.findChildren<QTimer *>("t1", Qt::FindDirectChildrenOnly);
+ QCOMPARE(tl.size(), 1);
+ QCOMPARE(tl.at(0), &t1);
+}
+
+
+class NotifyObject : public SenderObject, public ReceiverObject
+{
+public:
+ NotifyObject() : SenderObject(), ReceiverObject()
+ {}
+
+ QString org_signal;
+ QString nw_signal;
+
+protected:
+ void connectNotify( const char *signal )
+ {
+ org_signal = signal;
+ nw_signal = QMetaObject::normalizedSignature(signal);
+ };
+ void disconnectNotify( const char *signal )
+ {
+ org_signal = signal;
+ nw_signal = QMetaObject::normalizedSignature(signal);
+ };
+};
+
+void tst_QObject::connectDisconnectNotify_data()
+{
+ QTest::addColumn<QString>("a_signal");
+ QTest::addColumn<QString>("a_slot");
+
+ QTest::newRow("combo1") << SIGNAL( signal1() ) << SLOT( slot1() );
+ QTest::newRow("combo2") << SIGNAL( signal2(void) ) << SLOT( slot2( ) );
+ QTest::newRow("combo3") << SIGNAL( signal3( ) ) << SLOT( slot3(void) );
+ QTest::newRow("combo4") << SIGNAL( signal4( void ) )<< SLOT( slot4( void ) );
+}
+
+void tst_QObject::connectDisconnectNotify()
+{
+ NotifyObject *s = new NotifyObject;
+ NotifyObject *r = new NotifyObject;
+
+ QFETCH(QString, a_signal);
+ QFETCH(QString, a_slot);
+
+ // Test connectNotify
+ connect( (SenderObject*)s, a_signal.toLatin1(), (ReceiverObject*)r, a_slot.toLatin1() );
+ QCOMPARE( s->org_signal, s->nw_signal );
+ QCOMPARE( s->org_signal.toLatin1(), QMetaObject::normalizedSignature(a_signal.toLatin1().constData()) );
+
+ // Test disconnectNotify
+ QObject::disconnect( (SenderObject*)s, a_signal.toLatin1(), (ReceiverObject*)r, a_slot.toLatin1() );
+ QCOMPARE( s->org_signal, s->nw_signal );
+ QCOMPARE( s->org_signal.toLatin1(), QMetaObject::normalizedSignature(a_signal.toLatin1().constData()) );
+
+ // Reconnect
+ connect( (SenderObject*)s, a_signal.toLatin1(), (ReceiverObject*)r, a_slot.toLatin1() );
+ // Test disconnectNotify for a complete disconnect
+ ((SenderObject*)s)->disconnect((ReceiverObject*)r);
+
+ // Obtaining meta methods
+ int signalIndx = ((SenderObject*)s)->metaObject()->indexOfSignal(
+ QMetaObject::normalizedSignature(a_signal.toLatin1().constData()+1).constData());
+ int methodIndx = ((ReceiverObject*)r)->metaObject()->indexOfMethod(
+ QMetaObject::normalizedSignature(a_slot.toLatin1().constData()+1).constData());
+ QMetaMethod signal = ((SenderObject*)s)->metaObject()->method(signalIndx);
+ QMetaMethod method = ((ReceiverObject*)r)->metaObject()->method(methodIndx);
+
+ // Test connectNotify when connecting by QMetaMethod
+ connect( (SenderObject*)s, signal, (ReceiverObject*)r, method );
+ QCOMPARE( s->org_signal, s->nw_signal );
+ QCOMPARE( s->org_signal.toLatin1(), QMetaObject::normalizedSignature(a_signal.toLatin1().constData()) );
+
+ // Test disconnectNotify when disconnecting by QMetaMethod
+ QObject::disconnect( (SenderObject*)s, signal, (ReceiverObject*)r, method );
+ QCOMPARE( s->org_signal, s->nw_signal );
+ QCOMPARE( s->org_signal.toLatin1(), QMetaObject::normalizedSignature(a_signal.toLatin1().constData()) );
+
+ delete s;
+ delete r;
+}
+
+class SequenceObject : public ReceiverObject
+{
+ Q_OBJECT
+
+public:
+ QObject *next;
+ SequenceObject() : next(0) { }
+
+public slots:
+ void slot1_disconnectThis()
+ {
+ slot1();
+ disconnect(sender(), SIGNAL(signal1()), this, SLOT(slot1_disconnectThis()));
+ }
+
+ void slot2_reconnectThis()
+ {
+ slot2();
+
+ const QObject *s = sender();
+ disconnect(s, SIGNAL(signal1()), this, SLOT(slot2_reconnectThis()));
+ connect(s, SIGNAL(signal1()), this, SLOT(slot2_reconnectThis()));
+ }
+
+ void slot1_disconnectNext()
+ {
+ slot1();
+ disconnect(sender(), SIGNAL(signal1()), next, SLOT(slot1()));
+ }
+
+ void slot2_reconnectNext()
+ {
+ slot2();
+
+ // modify the connection list in 'this'
+ disconnect(sender(), SIGNAL(signal1()), next, SLOT(slot2()));
+ connect(sender(), SIGNAL(signal1()), next, SLOT(slot2()));
+
+ // modify the sender list in 'this'
+ connect(next, SIGNAL(destroyed()), this, SLOT(deleteLater()));
+ connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(deleteLater()));
+ disconnect(next, SIGNAL(destroyed()), this, SLOT(deleteLater()));
+ disconnect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(deleteLater()));
+ }
+
+ void slot1_deleteNext()
+ {
+ slot1();
+ delete next;
+ }
+
+ void slot2_deleteSender()
+ {
+ slot2();
+ delete sender();
+ }
+};
+
+void tst_QObject::emitInDefinedOrder()
+{
+ SenderObject sender;
+ ReceiverObject receiver1, receiver2, receiver3, receiver4;
+
+ connect(&sender, SIGNAL(signal1()), &receiver1, SLOT(slot1()));
+ connect(&sender, SIGNAL(signal1()), &receiver2, SLOT(slot1()));
+ connect(&sender, SIGNAL(signal1()), &receiver3, SLOT(slot1()));
+ connect(&sender, SIGNAL(signal1()), &receiver4, SLOT(slot1()));
+ connect(&sender, SIGNAL(signal1()), &receiver1, SLOT(slot2()));
+ connect(&sender, SIGNAL(signal1()), &receiver2, SLOT(slot2()));
+ connect(&sender, SIGNAL(signal1()), &receiver3, SLOT(slot2()));
+ connect(&sender, SIGNAL(signal1()), &receiver4, SLOT(slot2()));
+
+ int sequence;
+ ReceiverObject::sequence = sequence = 0;
+ sender.emitSignal1();
+ QCOMPARE(receiver1.sequence_slot1, ++sequence);
+ QCOMPARE(receiver2.sequence_slot1, ++sequence);
+ QCOMPARE(receiver3.sequence_slot1, ++sequence);
+ QCOMPARE(receiver4.sequence_slot1, ++sequence);
+ QCOMPARE(receiver1.sequence_slot2, ++sequence);
+ QCOMPARE(receiver2.sequence_slot2, ++sequence);
+ QCOMPARE(receiver3.sequence_slot2, ++sequence);
+ QCOMPARE(receiver4.sequence_slot2, ++sequence);
+
+ QObject::disconnect(&sender, SIGNAL(signal1()), &receiver2, SLOT(slot1()));
+ connect(&sender, SIGNAL(signal1()), &receiver2, SLOT(slot1()));
+
+ ReceiverObject::sequence = sequence = 0;
+ sender.emitSignal1();
+ QCOMPARE(receiver1.sequence_slot1, ++sequence);
+ QCOMPARE(receiver3.sequence_slot1, ++sequence);
+ QCOMPARE(receiver4.sequence_slot1, ++sequence);
+ QCOMPARE(receiver1.sequence_slot2, ++sequence);
+ QCOMPARE(receiver2.sequence_slot2, ++sequence);
+ QCOMPARE(receiver3.sequence_slot2, ++sequence);
+ QCOMPARE(receiver4.sequence_slot2, ++sequence);
+ QCOMPARE(receiver2.sequence_slot1, ++sequence);
+
+ QObject::disconnect(&sender, SIGNAL(signal1()), &receiver1, SLOT(slot1()));
+ connect(&sender, SIGNAL(signal1()), &receiver1, SLOT(slot1()));
+
+ ReceiverObject::sequence = sequence = 0;
+ sender.emitSignal1();
+ QCOMPARE(receiver3.sequence_slot1, ++sequence);
+ QCOMPARE(receiver4.sequence_slot1, ++sequence);
+ QCOMPARE(receiver1.sequence_slot2, ++sequence);
+ QCOMPARE(receiver2.sequence_slot2, ++sequence);
+ QCOMPARE(receiver3.sequence_slot2, ++sequence);
+ QCOMPARE(receiver4.sequence_slot2, ++sequence);
+ QCOMPARE(receiver2.sequence_slot1, ++sequence);
+ QCOMPARE(receiver1.sequence_slot1, ++sequence);
+
+ // ensure emission order even if the connections change during emission
+ SenderObject *sender2 = new SenderObject;
+ SequenceObject seq1, seq2, *seq3 = new SequenceObject, seq4;
+ seq1.next = &seq2;
+ seq2.next = seq3;
+ seq3->next = &seq4;
+
+ // try 1
+ connect(sender2, SIGNAL(signal1()), &seq1, SLOT(slot1_disconnectThis()));
+ connect(sender2, SIGNAL(signal1()), &seq2, SLOT(slot1_disconnectNext()));
+ connect(sender2, SIGNAL(signal1()), seq3, SLOT(slot1()));
+ connect(sender2, SIGNAL(signal1()), &seq4, SLOT(slot1()));
+ connect(sender2, SIGNAL(signal1()), &seq1, SLOT(slot2_reconnectThis()));
+ connect(sender2, SIGNAL(signal1()), &seq2, SLOT(slot2_reconnectNext()));
+ connect(sender2, SIGNAL(signal1()), seq3, SLOT(slot2()));
+ connect(sender2, SIGNAL(signal1()), &seq4, SLOT(slot2()));
+
+ SequenceObject::sequence = sequence = 0;
+ sender2->emitSignal1();
+ QCOMPARE(seq1.called(1), TRUE);
+ QCOMPARE(seq2.called(1), TRUE);
+ QCOMPARE(seq3->called(1), FALSE);
+ QCOMPARE(seq4.called(1), TRUE);
+ QCOMPARE(seq1.called(2), TRUE);
+ QCOMPARE(seq2.called(2), TRUE);
+ QCOMPARE(seq3->called(2), FALSE);
+ QCOMPARE(seq4.called(2), TRUE);
+ QCOMPARE(seq1.sequence_slot1, ++sequence);
+ QCOMPARE(seq2.sequence_slot1, ++sequence);
+ QCOMPARE(seq4.sequence_slot1, ++sequence);
+ QCOMPARE(seq1.sequence_slot2, ++sequence);
+ QCOMPARE(seq2.sequence_slot2, ++sequence);
+ QCOMPARE(seq4.sequence_slot2, ++sequence);
+
+ QObject::disconnect(sender2, 0, &seq1, 0);
+ QObject::disconnect(sender2, 0, &seq2, 0);
+ QObject::disconnect(sender2, 0, seq3, 0);
+ QObject::disconnect(sender2, 0, &seq4, 0);
+ seq1.reset();
+ seq2.reset();
+ seq3->reset();
+ seq4.reset();
+
+ // try 2
+ connect(sender2, SIGNAL(signal1()), &seq1, SLOT(slot2_reconnectThis()));
+ connect(sender2, SIGNAL(signal1()), &seq2, SLOT(slot2_reconnectNext()));
+ connect(sender2, SIGNAL(signal1()), seq3, SLOT(slot2()));
+ connect(sender2, SIGNAL(signal1()), &seq4, SLOT(slot2()));
+ connect(sender2, SIGNAL(signal1()), &seq1, SLOT(slot1_disconnectThis()));
+ connect(sender2, SIGNAL(signal1()), &seq2, SLOT(slot1_disconnectNext()));
+ connect(sender2, SIGNAL(signal1()), seq3, SLOT(slot1()));
+ connect(sender2, SIGNAL(signal1()), &seq4, SLOT(slot1()));
+
+ SequenceObject::sequence = sequence = 0;
+ sender2->emitSignal1();
+ QCOMPARE(seq1.called(2), TRUE);
+ QCOMPARE(seq2.called(2), TRUE);
+ QCOMPARE(seq3->called(2), FALSE);
+ QCOMPARE(seq4.called(2), TRUE);
+ QCOMPARE(seq1.called(1), TRUE);
+ QCOMPARE(seq2.called(1), TRUE);
+ QCOMPARE(seq3->called(1), FALSE);
+ QCOMPARE(seq4.called(1), TRUE);
+ QCOMPARE(seq1.sequence_slot2, ++sequence);
+ QCOMPARE(seq2.sequence_slot2, ++sequence);
+ QCOMPARE(seq4.sequence_slot2, ++sequence);
+ QCOMPARE(seq1.sequence_slot1, ++sequence);
+ QCOMPARE(seq2.sequence_slot1, ++sequence);
+ QCOMPARE(seq4.sequence_slot1, ++sequence);
+
+ QObject::disconnect(sender2, 0, &seq1, 0);
+ QObject::disconnect(sender2, 0, &seq2, 0);
+ QObject::disconnect(sender2, 0, seq3, 0);
+ QObject::disconnect(sender2, 0, &seq4, 0);
+ seq1.reset();
+ seq2.reset();
+ seq3->reset();
+ seq4.reset();
+
+ // try 3
+ connect(sender2, SIGNAL(signal1()), &seq1, SLOT(slot1()));
+ connect(sender2, SIGNAL(signal1()), &seq2, SLOT(slot1_disconnectNext()));
+ connect(sender2, SIGNAL(signal1()), seq3, SLOT(slot1()));
+ connect(sender2, SIGNAL(signal1()), &seq4, SLOT(slot1()));
+ connect(sender2, SIGNAL(signal1()), &seq1, SLOT(slot2()));
+ connect(sender2, SIGNAL(signal1()), &seq2, SLOT(slot2_reconnectNext()));
+ connect(sender2, SIGNAL(signal1()), seq3, SLOT(slot2()));
+ connect(sender2, SIGNAL(signal1()), &seq4, SLOT(slot2()));
+
+ SequenceObject::sequence = sequence = 0;
+ sender2->emitSignal1();
+ QCOMPARE(seq1.called(1), TRUE);
+ QCOMPARE(seq2.called(1), TRUE);
+ QCOMPARE(seq3->called(1), FALSE);
+ QCOMPARE(seq4.called(1), TRUE);
+ QCOMPARE(seq1.called(2), TRUE);
+ QCOMPARE(seq2.called(2), TRUE);
+ QCOMPARE(seq3->called(2), FALSE);
+ QCOMPARE(seq4.called(2), TRUE);
+ QCOMPARE(seq1.sequence_slot1, ++sequence);
+ QCOMPARE(seq2.sequence_slot1, ++sequence);
+ QCOMPARE(seq4.sequence_slot1, ++sequence);
+ QCOMPARE(seq1.sequence_slot2, ++sequence);
+ QCOMPARE(seq2.sequence_slot2, ++sequence);
+ QCOMPARE(seq4.sequence_slot2, ++sequence);
+
+ // ensure emission order even if objects are destroyed during emission
+ QObject::disconnect(sender2, 0, &seq1, 0);
+ QObject::disconnect(sender2, 0, &seq2, 0);
+ QObject::disconnect(sender2, 0, seq3, 0);
+ QObject::disconnect(sender2, 0, &seq4, 0);
+ seq1.reset();
+ seq2.reset();
+ seq3->reset();
+ seq4.reset();
+
+ connect(sender2, SIGNAL(signal1()), &seq1, SLOT(slot1()));
+ connect(sender2, SIGNAL(signal1()), &seq2, SLOT(slot1_deleteNext()));
+ connect(sender2, SIGNAL(signal1()), seq3, SLOT(slot1()));
+ connect(sender2, SIGNAL(signal1()), &seq4, SLOT(slot1()));
+ connect(sender2, SIGNAL(signal1()), &seq1, SLOT(slot2()));
+ connect(sender2, SIGNAL(signal1()), &seq2, SLOT(slot2_deleteSender()));
+ connect(sender2, SIGNAL(signal1()), seq3, SLOT(slot2()));
+ connect(sender2, SIGNAL(signal1()), &seq4, SLOT(slot2()));
+
+ QPointer<SenderObject> psender = sender2;
+ QPointer<SequenceObject> pseq3 = seq3;
+
+ SequenceObject::sequence = sequence = 0;
+ sender2->emitSignal1();
+ QCOMPARE(static_cast<QObject *>(psender), static_cast<QObject *>(0));
+ QCOMPARE(static_cast<QObject *>(pseq3), static_cast<QObject *>(0));
+ QCOMPARE(seq1.called(1), TRUE);
+ QCOMPARE(seq2.called(1), TRUE);
+ QCOMPARE(seq4.called(1), TRUE);
+ QCOMPARE(seq1.called(2), TRUE);
+ QCOMPARE(seq2.called(2), TRUE);
+ QCOMPARE(seq4.called(2), FALSE);
+ QCOMPARE(seq1.sequence_slot1, ++sequence);
+ QCOMPARE(seq2.sequence_slot1, ++sequence);
+ QCOMPARE(seq4.sequence_slot1, ++sequence);
+ QCOMPARE(seq1.sequence_slot2, ++sequence);
+ QCOMPARE(seq2.sequence_slot2, ++sequence);
+
+ QPointer<SenderObject> psender3 = new SenderObject;
+ connect(psender3, SIGNAL(signal1()), psender3, SIGNAL(signal2()));
+ connect(psender3, SIGNAL(signal2()), &seq1, SLOT(slot2_deleteSender()));
+ psender3->emitSignal1();
+ QVERIFY(!psender3);
+}
+
+static int instanceCount = 0;
+
+struct CustomType
+{
+ CustomType(int l1 = 0, int l2 = 0, int l3 = 0): i1(l1), i2(l2), i3(l3)
+ { ++instanceCount; }
+ CustomType(const CustomType &other): i1(other.i1), i2(other.i2), i3(other.i3)
+ { ++instanceCount; }
+ ~CustomType() { --instanceCount; }
+
+ int i1, i2, i3;
+ int value() { return i1 + i2 + i3; }
+};
+
+Q_DECLARE_METATYPE(CustomType*)
+
+class QCustomTypeChecker: public QObject
+{
+ Q_OBJECT
+
+public:
+ QCustomTypeChecker(QObject *parent = 0): QObject(parent) {}
+ void doEmit(CustomType ct)
+ { emit signal1(ct); }
+
+public slots:
+ void slot1(CustomType ct);
+
+signals:
+ void signal1(CustomType ct);
+
+public:
+ CustomType received;
+};
+
+void QCustomTypeChecker::slot1(CustomType ct)
+{ received = ct; }
+
+
+void tst_QObject::customTypes()
+{
+ CustomType t0;
+ CustomType t1(1, 2, 3);
+ CustomType t2(2, 3, 4);
+
+ {
+ QCustomTypeChecker checker;
+ QCOMPARE(instanceCount, 4);
+
+ connect(&checker, SIGNAL(signal1(CustomType)), &checker, SLOT(slot1(CustomType)),
+ Qt::DirectConnection);
+ QCOMPARE(checker.received.value(), 0);
+ checker.doEmit(t1);
+ QCOMPARE(checker.received.value(), t1.value());
+ checker.received = t0;
+
+ int idx = qRegisterMetaType<CustomType>("CustomType");
+ QCOMPARE(QMetaType::type("CustomType"), idx);
+
+ checker.disconnect();
+ connect(&checker, SIGNAL(signal1(CustomType)), &checker, SLOT(slot1(CustomType)),
+ Qt::QueuedConnection);
+ QCOMPARE(instanceCount, 4);
+ checker.doEmit(t2);
+ QCOMPARE(instanceCount, 5);
+ QCOMPARE(checker.received.value(), t0.value());
+
+ QCoreApplication::processEvents();
+ QCOMPARE(checker.received.value(), t2.value());
+ QCOMPARE(instanceCount, 4);
+
+ QVERIFY(QMetaType::isRegistered(idx));
+ QCOMPARE(qRegisterMetaType<CustomType>("CustomType"), idx);
+ QCOMPARE(QMetaType::type("CustomType"), idx);
+ QVERIFY(QMetaType::isRegistered(idx));
+ }
+ QCOMPARE(instanceCount, 3);
+}
+
+QDataStream &operator<<(QDataStream &stream, const CustomType &ct)
+{
+ stream << ct.i1 << ct.i2 << ct.i3;
+ return stream;
+}
+
+QDataStream &operator>>(QDataStream &stream, CustomType &ct)
+{
+ stream >> ct.i1;
+ stream >> ct.i2;
+ stream >> ct.i3;
+ return stream;
+}
+
+void tst_QObject::streamCustomTypes()
+{
+ QByteArray ba;
+
+ int idx = qRegisterMetaType<CustomType>("CustomType");
+ qRegisterMetaTypeStreamOperators<CustomType>("CustomType");
+
+ {
+ CustomType t1(1, 2, 3);
+ QCOMPARE(instanceCount, 1);
+ QDataStream stream(&ba, (QIODevice::OpenMode)QIODevice::WriteOnly);
+ QMetaType::save(stream, idx, &t1);
+ }
+
+ QCOMPARE(instanceCount, 0);
+
+ {
+ CustomType t2;
+ QCOMPARE(instanceCount, 1);
+ QDataStream stream(&ba, (QIODevice::OpenMode)QIODevice::ReadOnly);
+ QMetaType::load(stream, idx, &t2);
+ QCOMPARE(instanceCount, 1);
+ QCOMPARE(t2.i1, 1);
+ QCOMPARE(t2.i2, 2);
+ QCOMPARE(t2.i3, 3);
+ }
+ QCOMPARE(instanceCount, 0);
+}
+
+typedef QString CustomString;
+
+class PropertyObject : public QObject
+{
+ Q_OBJECT
+ Q_ENUMS(Alpha Priority)
+
+ Q_PROPERTY(Alpha alpha READ alpha WRITE setAlpha)
+ Q_PROPERTY(Priority priority READ priority WRITE setPriority)
+ Q_PROPERTY(int number READ number WRITE setNumber)
+ Q_PROPERTY(QString string READ string WRITE setString)
+ Q_PROPERTY(QVariant variant READ variant WRITE setVariant)
+ Q_PROPERTY(CustomType* custom READ custom WRITE setCustom)
+ Q_PROPERTY(float myFloat READ myFloat WRITE setMyFloat)
+ Q_PROPERTY(qreal myQReal READ myQReal WRITE setMyQReal)
+ Q_PROPERTY(CustomString customString READ customString WRITE setCustomString )
+
+public:
+ enum Alpha {
+ Alpha0,
+ Alpha1,
+ Alpha2
+ };
+
+ enum Priority { High, Low, VeryHigh, VeryLow };
+
+ PropertyObject()
+ : m_alpha(Alpha0), m_priority(High), m_number(0), m_custom(0), m_float(42)
+ {}
+
+ Alpha alpha() const { return m_alpha; }
+ void setAlpha(Alpha alpha) { m_alpha = alpha; }
+
+ Priority priority() const { return m_priority; }
+ void setPriority(Priority priority) { m_priority = priority; }
+
+ int number() const { return m_number; }
+ void setNumber(int number) { m_number = number; }
+
+ QString string() const { return m_string; }
+ void setString(const QString &string) { m_string = string; }
+
+ QVariant variant() const { return m_variant; }
+ void setVariant(const QVariant &variant) { m_variant = variant; }
+
+ CustomType *custom() const { return m_custom; }
+ void setCustom(CustomType *custom) { m_custom = custom; }
+
+ void setMyFloat(float value) { m_float = value; }
+ inline float myFloat() const { return m_float; }
+
+ void setMyQReal(qreal value) { m_qreal = value; }
+ qreal myQReal() const { return m_qreal; }
+
+ CustomString customString() const { return m_customString; }
+ void setCustomString(const QString &string) { m_customString = string; }
+
+private:
+ Alpha m_alpha;
+ Priority m_priority;
+ int m_number;
+ QString m_string;
+ QVariant m_variant;
+ CustomType *m_custom;
+ float m_float;
+ qreal m_qreal;
+ CustomString m_customString;
+};
+
+Q_DECLARE_METATYPE(PropertyObject::Priority)
+
+void tst_QObject::threadSignalEmissionCrash()
+{
+#if defined(Q_OS_WINCE) || defined(Q_OS_SYMBIAN)
+ int loopCount = 100;
+#else
+ int loopCount = 1000;
+#endif
+ for (int i = 0; i < loopCount; ++i) {
+ QTcpSocket socket;
+ socket.connectToHost("localhost", 80);
+ }
+}
+
+class TestThread : public QThread
+{
+ Q_OBJECT
+public:
+ inline void run()
+ {
+ *object = new QObject;
+ *child = new QObject(*object);
+ mutex.lock();
+ cond.wakeOne();
+ cond.wait(&mutex);
+ mutex.unlock();
+ }
+
+ QObject **object, **child;
+ QMutex mutex;
+ QWaitCondition cond;
+};
+
+void tst_QObject::thread()
+{
+ QThread *currentThread = QThread::currentThread();
+ // the current thread is the same as the QApplication
+ // thread... see tst_QApplication::thread()
+
+ {
+ QObject object;
+ // thread affinity for objects with no parent should be the
+ // current thread
+ QVERIFY(object.thread() != 0);
+ QCOMPARE(object.thread(), currentThread);
+ // children inherit their parent's thread
+ QObject child(&object);
+ QCOMPARE(child.thread(), object.thread());
+ }
+
+ QObject *object = 0;
+ QObject *child = 0;
+
+ {
+ TestThread thr;
+ QVERIFY(thr.thread() != 0);
+ QCOMPARE(thr.thread(), currentThread);
+
+ thr.object = &object;
+ thr.child = &child;
+
+ thr.mutex.lock();
+ thr.start();
+ thr.cond.wait(&thr.mutex);
+
+ // thread affinity for an object with no parent should be the
+ // thread in which the object was created
+ QCOMPARE(object->thread(), (QThread *)&thr);
+ // children inherit their parent's thread
+ QCOMPARE(child->thread(), object->thread());
+
+ thr.cond.wakeOne();
+ thr.mutex.unlock();
+ thr.wait();
+
+ // even though the thread is no longer running, the affinity
+ // should not change
+ QCOMPARE(object->thread(), (QThread *)&thr);
+ QCOMPARE(child->thread(), object->thread());
+ }
+
+ // the thread has been destroyed, thread affinity should
+ // automatically reset to no thread
+ QCOMPARE(object->thread(), (QThread *)0);
+ QCOMPARE(child->thread(), object->thread());
+
+ delete object;
+}
+
+class MoveToThreadObject : public QObject
+{
+ Q_OBJECT
+public:
+ QThread *timerEventThread;
+ QThread *customEventThread;
+ QThread *slotThread;
+
+ MoveToThreadObject(QObject *parent = 0)
+ : QObject(parent), timerEventThread(0), customEventThread(0), slotThread(0)
+ { }
+
+ void customEvent(QEvent *)
+ {
+ if (customEventThread)
+ qFatal("%s: customEventThread should be null", Q_FUNC_INFO);
+ customEventThread = QThread::currentThread();
+ emit theSignal();
+ }
+
+ void timerEvent(QTimerEvent *)
+ {
+ if (timerEventThread)
+ qFatal("%s: timerEventThread should be null", Q_FUNC_INFO);
+ timerEventThread = QThread::currentThread();
+ emit theSignal();
+ }
+
+public slots:
+ void theSlot()
+ {
+ if (slotThread)
+ qFatal("%s: slotThread should be null", Q_FUNC_INFO);
+ slotThread = QThread::currentThread();
+ emit theSignal();
+ }
+
+signals:
+ void theSignal();
+};
+
+class MoveToThreadThread : public QThread
+{
+public:
+ ~MoveToThreadThread()
+ {
+ if (isRunning()) {
+ terminate();
+ wait();
+ }
+ }
+ void start()
+ {
+ QEventLoop eventLoop;
+ connect(this, SIGNAL(started()), &eventLoop, SLOT(quit()), Qt::QueuedConnection);
+ QThread::start();
+ // wait for thread to start
+ (void) eventLoop.exec();
+ }
+ void run()
+ { (void) exec(); }
+};
+
+void tst_QObject::thread0()
+{
+ QObject *object = new QObject;
+ object->moveToThread(0);
+ QObject *child = new QObject(object);
+ QCOMPARE(child->parent(), object);
+ QCOMPARE(child->thread(), (QThread *)0);
+
+#if 0
+ // We don't support moving children into a parent that has no thread
+ // affinity (yet?).
+ QObject *child2 = new QObject;
+ child2->moveToThread(0);
+ child2->setParent(object);
+ QCOMPARE(child2->parent(), object);
+ QCOMPARE(child2->thread(), (QThread *)0);
+#endif
+
+ delete object;
+}
+
+void tst_QObject::moveToThread()
+{
+ QThread *currentThread = QThread::currentThread();
+
+ {
+ QObject *object = new QObject;
+ QObject *child = new QObject(object);
+ QCOMPARE(object->thread(), currentThread);
+ QCOMPARE(child->thread(), currentThread);
+ object->moveToThread(0);
+ QCOMPARE(object->thread(), (QThread *)0);
+ QCOMPARE(child->thread(), (QThread *)0);
+ object->moveToThread(currentThread);
+ QCOMPARE(object->thread(), currentThread);
+ QCOMPARE(child->thread(), currentThread);
+ object->moveToThread(0);
+ QCOMPARE(object->thread(), (QThread *)0);
+ QCOMPARE(child->thread(), (QThread *)0);
+ // can delete an object with no thread anywhere
+ delete object;
+ }
+
+ {
+ MoveToThreadThread thread;
+ thread.start();
+
+ QObject *object = new QObject;
+ QObject *child = new QObject(object);
+ QPointer<QObject> opointer = object;
+ QPointer<QObject> cpointer = object;
+
+ QCOMPARE(object->thread(), currentThread);
+ QCOMPARE(child->thread(), currentThread);
+ object->moveToThread(&thread);
+ QCOMPARE(object->thread(), (QThread *)&thread);
+ QCOMPARE(child->thread(), (QThread *)&thread);
+
+ connect(object, SIGNAL(destroyed()), &thread, SLOT(quit()), Qt::DirectConnection);
+ QMetaObject::invokeMethod(object, "deleteLater", Qt::QueuedConnection);
+ thread.wait();
+
+ QVERIFY(opointer == 0);
+ QVERIFY(cpointer == 0);
+ }
+
+ {
+ // make sure posted events are moved with the object
+ MoveToThreadThread thread;
+ thread.start();
+
+ MoveToThreadObject *object = new MoveToThreadObject;
+ MoveToThreadObject *child = new MoveToThreadObject(object);
+
+ connect(object, SIGNAL(theSignal()), &thread, SLOT(quit()), Qt::DirectConnection);
+ QCoreApplication::postEvent(child, new QEvent(QEvent::User));
+ QCoreApplication::postEvent(object, new QEvent(QEvent::User));
+
+ QCOMPARE(object->thread(), currentThread);
+ QCOMPARE(child->thread(), currentThread);
+ object->moveToThread(&thread);
+ QCOMPARE(object->thread(), (QThread *)&thread);
+ QCOMPARE(child->thread(), (QThread *)&thread);
+
+ thread.wait();
+
+ QCOMPARE(object->customEventThread, (QThread *)&thread);
+ QCOMPARE(child->customEventThread, (QThread *)&thread);
+
+ thread.start();
+ connect(object, SIGNAL(destroyed()), &thread, SLOT(quit()), Qt::DirectConnection);
+ QMetaObject::invokeMethod(object, "deleteLater", Qt::QueuedConnection);
+ thread.wait();
+ }
+
+ {
+ // make sure timers are moved with the object
+ MoveToThreadThread thread;
+ thread.start();
+
+ MoveToThreadObject *object = new MoveToThreadObject;
+ MoveToThreadObject *child = new MoveToThreadObject(object);
+
+ connect(object, SIGNAL(theSignal()), &thread, SLOT(quit()), Qt::DirectConnection);
+
+#if defined(Q_OS_SYMBIAN)
+ // Child timer will be registered after parent timer in the new
+ // thread, and 10ms is less than symbian timer resolution, so
+ // child->timerEventThread compare after thread.wait() will
+ // usually fail unless timers are farther apart.
+ child->startTimer(100);
+ object->startTimer(150);
+#else
+ child->startTimer(90);
+ object->startTimer(100);
+#endif
+
+ QCOMPARE(object->thread(), currentThread);
+ QCOMPARE(child->thread(), currentThread);
+ object->moveToThread(&thread);
+ QCOMPARE(object->thread(), (QThread *)&thread);
+ QCOMPARE(child->thread(), (QThread *)&thread);
+
+ thread.wait();
+
+ QCOMPARE(object->timerEventThread, (QThread *)&thread);
+ QCOMPARE(child->timerEventThread, (QThread *)&thread);
+
+ thread.start();
+ connect(object, SIGNAL(destroyed()), &thread, SLOT(quit()), Qt::DirectConnection);
+ QMetaObject::invokeMethod(object, "deleteLater", Qt::QueuedConnection);
+ thread.wait();
+ }
+
+ {
+ // make sure socket notifiers are moved with the object
+ MoveToThreadThread thread;
+ thread.start();
+
+ QTcpServer server;
+ QVERIFY(server.listen(QHostAddress::LocalHost, 0));
+ QTcpSocket *socket = new QTcpSocket;
+ MoveToThreadObject *child = new MoveToThreadObject(socket);
+ connect(socket, SIGNAL(disconnected()), child, SLOT(theSlot()), Qt::DirectConnection);
+ connect(child, SIGNAL(theSignal()), &thread, SLOT(quit()), Qt::DirectConnection);
+
+ socket->connectToHost(server.serverAddress(), server.serverPort());
+
+ QVERIFY(server.waitForNewConnection(1000));
+ QTcpSocket *serverSocket = server.nextPendingConnection();
+ QVERIFY(serverSocket);
+
+ socket->waitForConnected();
+
+ QCOMPARE(socket->thread(), currentThread);
+ socket->moveToThread(&thread);
+ QCOMPARE(socket->thread(), (QThread *)&thread);
+
+ serverSocket->close();
+
+ QVERIFY(thread.wait(10000));
+
+ QCOMPARE(child->slotThread, (QThread *)&thread);
+
+ thread.start();
+ connect(socket, SIGNAL(destroyed()), &thread, SLOT(quit()), Qt::DirectConnection);
+ QMetaObject::invokeMethod(socket, "deleteLater", Qt::QueuedConnection);
+ thread.wait();
+ }
+}
+
+
+void tst_QObject::property()
+{
+ PropertyObject object;
+ const QMetaObject *mo = object.metaObject();
+ QMetaProperty property;
+ QVERIFY(mo);
+
+ QVERIFY(mo->indexOfProperty("alpha") != -1);
+ property = mo->property(mo->indexOfProperty("alpha"));
+ QVERIFY(property.isEnumType());
+ QCOMPARE(property.typeName(), "Alpha");
+ QCOMPARE(property.type(), QVariant::Int);
+
+ QVariant var = object.property("alpha");
+ QVERIFY(!var.isNull());
+ QCOMPARE(var.toInt(), int(PropertyObject::Alpha0));
+ object.setAlpha(PropertyObject::Alpha1);
+ QCOMPARE(object.property("alpha").toInt(), int(PropertyObject::Alpha1));
+ QVERIFY(object.setProperty("alpha", PropertyObject::Alpha2));
+ QCOMPARE(object.property("alpha").toInt(), int(PropertyObject::Alpha2));
+ QVERIFY(object.setProperty("alpha", "Alpha1"));
+ QCOMPARE(object.property("alpha").toInt(), int(PropertyObject::Alpha1));
+ QVERIFY(!object.setProperty("alpha", QVariant()));
+
+ QVERIFY(mo->indexOfProperty("number") != -1);
+ QCOMPARE(object.property("number").toInt(), 0);
+ object.setNumber(24);
+ QCOMPARE(object.property("number"), QVariant(24));
+ QVERIFY(object.setProperty("number", 12));
+ QCOMPARE(object.property("number"), QVariant(12));
+ QVERIFY(object.setProperty("number", "42"));
+ QCOMPARE(object.property("number"), QVariant(42));
+
+ QVERIFY(mo->indexOfProperty("string") != -1);
+ QCOMPARE(object.property("string").toString(), QString());
+ object.setString("String1");
+ QCOMPARE(object.property("string"), QVariant("String1"));
+ QVERIFY(object.setProperty("string", "String2"));
+ QCOMPARE(object.property("string"), QVariant("String2"));
+ QVERIFY(!object.setProperty("string", QVariant()));
+
+ const int idx = mo->indexOfProperty("variant");
+ QVERIFY(idx != -1);
+ QVERIFY(mo->property(idx).type() == QVariant::LastType);
+ QCOMPARE(object.property("variant"), QVariant());
+ QVariant variant1(42);
+ QVariant variant2("string");
+ object.setVariant(variant1);
+ QCOMPARE(object.property("variant"), variant1);
+ QVERIFY(object.setProperty("variant", variant2));
+ QCOMPARE(object.variant(), QVariant(variant2));
+ QCOMPARE(object.property("variant"), variant2);
+ QVERIFY(object.setProperty("variant", QVariant()));
+ QCOMPARE(object.property("variant"), QVariant());
+
+ QVERIFY(mo->indexOfProperty("custom") != -1);
+ property = mo->property(mo->indexOfProperty("custom"));
+ QVERIFY(property.isValid());
+ QVERIFY(property.isWritable());
+ QVERIFY(!property.isEnumType());
+ QCOMPARE(property.typeName(), "CustomType*");
+ QCOMPARE(property.type(), QVariant::UserType);
+
+ CustomType *customPointer = 0;
+ QVariant customVariant = object.property("custom");
+ customPointer = qVariantValue<CustomType *>(customVariant);
+ QCOMPARE(customPointer, object.custom());
+
+ CustomType custom;
+ customPointer = &custom;
+ qVariantSetValue(customVariant, customPointer);
+
+ property = mo->property(mo->indexOfProperty("custom"));
+ QVERIFY(property.isWritable());
+ QCOMPARE(property.typeName(), "CustomType*");
+ QCOMPARE(property.type(), QVariant::UserType);
+
+ QVERIFY(object.setProperty("custom", customVariant));
+ QCOMPARE(object.custom(), customPointer);
+
+ customVariant = object.property("custom");
+ customPointer = qVariantValue<CustomType *>(customVariant);
+ QCOMPARE(object.custom(), customPointer);
+
+ // this enum property has a meta type, but it's not yet registered, so we know this fails
+ QVERIFY(mo->indexOfProperty("priority") != -1);
+ property = mo->property(mo->indexOfProperty("priority"));
+ QVERIFY(property.isEnumType());
+ QCOMPARE(property.typeName(), "Priority");
+ QCOMPARE(property.type(), QVariant::Int);
+
+ var = object.property("priority");
+ QVERIFY(!var.isNull());
+ QCOMPARE(var.toInt(), int(PropertyObject::High));
+ object.setPriority(PropertyObject::Low);
+ QCOMPARE(object.property("priority").toInt(), int(PropertyObject::Low));
+ QVERIFY(object.setProperty("priority", PropertyObject::VeryHigh));
+ QCOMPARE(object.property("priority").toInt(), int(PropertyObject::VeryHigh));
+ QVERIFY(object.setProperty("priority", "High"));
+ QCOMPARE(object.property("priority").toInt(), int(PropertyObject::High));
+ QVERIFY(!object.setProperty("priority", QVariant()));
+
+ // now it's registered, so it works as expected
+ int priorityMetaTypeId = qRegisterMetaType<PropertyObject::Priority>("PropertyObject::Priority");
+
+ QVERIFY(mo->indexOfProperty("priority") != -1);
+ property = mo->property(mo->indexOfProperty("priority"));
+ QVERIFY(property.isEnumType());
+ QCOMPARE(property.typeName(), "Priority");
+ QCOMPARE(property.type(), QVariant::UserType);
+ QCOMPARE(property.userType(), priorityMetaTypeId);
+
+ var = object.property("priority");
+ QVERIFY(!var.isNull());
+ QVERIFY(qVariantCanConvert<PropertyObject::Priority>(var));
+ QCOMPARE(qVariantValue<PropertyObject::Priority>(var), PropertyObject::High);
+ object.setPriority(PropertyObject::Low);
+ QCOMPARE(qVariantValue<PropertyObject::Priority>(object.property("priority")), PropertyObject::Low);
+ QVERIFY(object.setProperty("priority", PropertyObject::VeryHigh));
+ QCOMPARE(qVariantValue<PropertyObject::Priority>(object.property("priority")), PropertyObject::VeryHigh);
+ QVERIFY(object.setProperty("priority", "High"));
+ QCOMPARE(qVariantValue<PropertyObject::Priority>(object.property("priority")), PropertyObject::High);
+ QVERIFY(!object.setProperty("priority", QVariant()));
+
+ var = object.property("priority");
+ QCOMPARE(qVariantValue<PropertyObject::Priority>(var), PropertyObject::High);
+ object.setPriority(PropertyObject::Low);
+ QCOMPARE(qVariantValue<PropertyObject::Priority>(object.property("priority")), PropertyObject::Low);
+ object.setProperty("priority", var);
+ QCOMPARE(qVariantValue<PropertyObject::Priority>(object.property("priority")), PropertyObject::High);
+
+ qRegisterMetaType<CustomString>("CustomString");
+ QVERIFY(mo->indexOfProperty("customString") != -1);
+ QCOMPARE(object.property("customString").toString(), QString());
+ object.setCustomString("String1");
+ QCOMPARE(object.property("customString"), QVariant("String1"));
+ QVERIFY(object.setProperty("customString", "String2"));
+ QCOMPARE(object.property("customString"), QVariant("String2"));
+ QVERIFY(!object.setProperty("customString", QVariant()));
+}
+
+void tst_QObject::metamethod()
+{
+ SenderObject obj;
+ const QMetaObject *mobj = obj.metaObject();
+ QMetaMethod m;
+
+ m = mobj->method(mobj->indexOfMethod("invoke1()"));
+ QVERIFY(QByteArray(m.signature()) == "invoke1()");
+ QVERIFY(m.methodType() == QMetaMethod::Method);
+ QVERIFY(m.access() == QMetaMethod::Public);
+ QVERIFY(!(m.attributes() & QMetaMethod::Scriptable));
+ QVERIFY(!(m.attributes() & QMetaMethod::Compatibility));
+
+ m = mobj->method(mobj->indexOfMethod("sinvoke1()"));
+ QVERIFY(QByteArray(m.signature()) == "sinvoke1()");
+ QVERIFY(m.methodType() == QMetaMethod::Method);
+ QVERIFY(m.access() == QMetaMethod::Public);
+ QVERIFY((m.attributes() & QMetaMethod::Scriptable));
+ QVERIFY(!(m.attributes() & QMetaMethod::Compatibility));
+
+ m = mobj->method(mobj->indexOfMethod("invoke2()"));
+ QVERIFY(QByteArray(m.signature()) == "invoke2()");
+ QVERIFY(m.methodType() == QMetaMethod::Method);
+ QVERIFY(m.access() == QMetaMethod::Protected);
+ QVERIFY(!(m.attributes() & QMetaMethod::Scriptable));
+ QVERIFY((m.attributes() & QMetaMethod::Compatibility));
+
+ m = mobj->method(mobj->indexOfMethod("sinvoke2()"));
+ QVERIFY(QByteArray(m.signature()) == "sinvoke2()");
+ QVERIFY(m.methodType() == QMetaMethod::Method);
+ QVERIFY(m.access() == QMetaMethod::Protected);
+ QVERIFY((m.attributes() & QMetaMethod::Scriptable));
+ QVERIFY((m.attributes() & QMetaMethod::Compatibility));
+
+ m = mobj->method(mobj->indexOfMethod("invoke3()"));
+ QVERIFY(QByteArray(m.signature()) == "invoke3()");
+ QVERIFY(m.methodType() == QMetaMethod::Method);
+ QVERIFY(m.access() == QMetaMethod::Private);
+ QVERIFY(!(m.attributes() & QMetaMethod::Scriptable));
+ QVERIFY(!(m.attributes() & QMetaMethod::Compatibility));
+
+ m = mobj->method(mobj->indexOfMethod("sinvoke3()"));
+ QVERIFY(QByteArray(m.signature()) == "sinvoke3()");
+ QVERIFY(m.methodType() == QMetaMethod::Method);
+ QVERIFY(m.access() == QMetaMethod::Private);
+ QVERIFY((m.attributes() & QMetaMethod::Scriptable));
+ QVERIFY(!(m.attributes() & QMetaMethod::Compatibility));
+
+ m = mobj->method(mobj->indexOfMethod("signal5()"));
+ QVERIFY(QByteArray(m.signature()) == "signal5()");
+ QVERIFY(m.methodType() == QMetaMethod::Signal);
+ QVERIFY(m.access() == QMetaMethod::Protected);
+ QVERIFY(!(m.attributes() & QMetaMethod::Scriptable));
+ QVERIFY((m.attributes() & QMetaMethod::Compatibility));
+
+ m = mobj->method(mobj->indexOfMethod("aPublicSlot()"));
+ QVERIFY(QByteArray(m.signature()) == "aPublicSlot()");
+ QVERIFY(m.methodType() == QMetaMethod::Slot);
+ QVERIFY(m.access() == QMetaMethod::Public);
+ QVERIFY(!(m.attributes() & QMetaMethod::Scriptable));
+ QVERIFY(!(m.attributes() & QMetaMethod::Compatibility));
+
+ m = mobj->method(mobj->indexOfMethod("invoke1()"));
+ QCOMPARE(m.parameterNames().count(), 0);
+ QCOMPARE(m.parameterTypes().count(), 0);
+
+ m = mobj->method(mobj->indexOfMethod("invoke2(int)"));
+ QCOMPARE(m.parameterNames().count(), 1);
+ QCOMPARE(m.parameterTypes().count(), 1);
+ QCOMPARE(m.parameterTypes().at(0), QByteArray("int"));
+ QVERIFY(m.parameterNames().at(0).isEmpty());
+
+ m = mobj->method(mobj->indexOfMethod("invoke3(int,int)"));
+ QCOMPARE(m.parameterNames().count(), 2);
+ QCOMPARE(m.parameterTypes().count(), 2);
+ QCOMPARE(m.parameterTypes().at(0), QByteArray("int"));
+ QCOMPARE(m.parameterNames().at(0), QByteArray("hinz"));
+ QCOMPARE(m.parameterTypes().at(1), QByteArray("int"));
+ QCOMPARE(m.parameterNames().at(1), QByteArray("kunz"));
+
+}
+
+namespace QObjectTest
+{
+ class TestObject: public QObject
+ {
+ Q_OBJECT
+ public:
+ TestObject(): QObject(), i(0) {}
+ void doEmit() { emit aSignal(); }
+ int i;
+ public slots:
+ void aSlot() { ++i; }
+ signals:
+ void aSignal();
+ };
+}
+
+void tst_QObject::namespaces()
+{
+ QObjectTest::TestObject obj;
+
+ QVERIFY(connect(&obj, SIGNAL(aSignal()), &obj, SLOT(aSlot())));
+ obj.doEmit();
+ QCOMPARE(obj.i, 1);
+}
+
+class SuperObject : public QObject
+{
+ Q_OBJECT
+public:
+ QObject *theSender;
+ int theSignalId;
+
+ SuperObject()
+ {
+ theSender = 0;
+ theSignalId = 0;
+ }
+
+ friend class tst_QObject;
+
+ using QObject::sender;
+
+public slots:
+ void rememberSender()
+ {
+ theSender = sender();
+ theSignalId = senderSignalIndex();
+ }
+
+ void deleteAndRememberSender()
+ {
+ delete theSender;
+ rememberSender();
+ }
+signals:
+ void anotherSignal();
+ void theSignal();
+};
+
+void tst_QObject::sender()
+{
+ {
+ SuperObject sender;
+ SuperObject receiver;
+ connect(&sender, SIGNAL(anotherSignal()),
+ &receiver, SLOT(rememberSender()));
+ connect(&sender, SIGNAL(theSignal()),
+ &receiver, SLOT(rememberSender()));
+ QCOMPARE(receiver.sender(), (QObject *)0);
+ QCOMPARE(receiver.senderSignalIndex(), -1);
+ emit sender.theSignal();
+ QCOMPARE(receiver.theSender, (QObject *)&sender);
+ QCOMPARE(receiver.sender(), (QObject *)0);
+ QCOMPARE(receiver.theSignalId,
+ sender.metaObject()->indexOfSignal("theSignal()"));
+ QCOMPARE(receiver.senderSignalIndex(), -1);
+
+ emit sender.anotherSignal();
+ QCOMPARE(receiver.theSignalId,
+ sender.metaObject()->indexOfSignal("anotherSignal()"));
+ QCOMPARE(receiver.senderSignalIndex(), -1);
+ }
+
+ {
+ SuperObject *sender = new SuperObject;
+ SuperObject *receiver = new SuperObject;
+ connect(sender, SIGNAL(theSignal()),
+ receiver, SLOT(rememberSender()),
+ Qt::BlockingQueuedConnection);
+
+ QThread thread;
+ receiver->moveToThread(&thread);
+ connect(sender, SIGNAL(theSignal()),
+ &thread, SLOT(quit()),
+ Qt::DirectConnection);
+
+ QCOMPARE(receiver->sender(), (QObject *)0);
+ QCOMPARE(receiver->senderSignalIndex(), -1);
+ receiver->theSender = 0;
+ receiver->theSignalId = -1;
+ thread.start();
+ emit sender->theSignal();
+ QCOMPARE(receiver->theSender, (QObject *) sender);
+ QCOMPARE(receiver->sender(), (QObject *)0);
+ QCOMPARE(receiver->theSignalId,
+ sender->metaObject()->indexOfSignal("theSignal()"));
+ QCOMPARE(receiver->senderSignalIndex(), -1);
+
+ QVERIFY(thread.wait(10000));
+ delete receiver;
+ delete sender;
+ }
+
+ {
+ SuperObject *sender = new SuperObject;
+ SuperObject receiver;
+ connect(sender, SIGNAL(theSignal()),
+ &receiver, SLOT(deleteAndRememberSender()));
+ QCOMPARE(receiver.sender(), (QObject *)0);
+ receiver.theSender = sender;
+ emit sender->theSignal();
+ QCOMPARE(receiver.theSender, (QObject *)0);
+ QCOMPARE(receiver.sender(), (QObject *)0);
+ }
+
+ {
+ SuperObject *sender = new SuperObject;
+ SuperObject *receiver = new SuperObject;
+ connect(sender, SIGNAL(theSignal()),
+ receiver, SLOT(deleteAndRememberSender()),
+ Qt::BlockingQueuedConnection);
+
+ QThread thread;
+ receiver->moveToThread(&thread);
+ connect(sender, SIGNAL(destroyed()),
+ &thread, SLOT(quit()),
+ Qt::DirectConnection);
+
+ QCOMPARE(receiver->sender(), (QObject *)0);
+ receiver->theSender = sender;
+ thread.start();
+ emit sender->theSignal();
+ QCOMPARE(receiver->theSender, (QObject *)0);
+ QCOMPARE(receiver->sender(), (QObject *)0);
+
+ QVERIFY(thread.wait(10000));
+ delete receiver;
+ }
+}
+
+namespace Foo
+{
+ struct Bar
+ {
+ virtual ~Bar() {}
+ virtual int rtti() const = 0;
+ };
+
+ struct Bleh
+ {
+ virtual ~Bleh() {}
+ virtual int rtti() const = 0;
+ };
+}
+
+QT_BEGIN_NAMESPACE
+Q_DECLARE_INTERFACE(Foo::Bar, "com.qtest.foobar")
+QT_END_NAMESPACE
+
+#define Bleh_iid "com.qtest.bleh"
+QT_BEGIN_NAMESPACE
+Q_DECLARE_INTERFACE(Foo::Bleh, Bleh_iid)
+QT_END_NAMESPACE
+
+class FooObject: public QObject, public Foo::Bar
+{
+ Q_OBJECT
+ Q_INTERFACES(Foo::Bar)
+public:
+ int rtti() const { return 42; }
+};
+
+class BlehObject : public QObject, public Foo::Bleh
+{
+ Q_OBJECT
+ Q_INTERFACES(Foo::Bleh)
+public:
+ int rtti() const { return 43; }
+};
+
+void tst_QObject::declareInterface()
+{
+ FooObject obj;
+
+ Foo::Bar *bar = qobject_cast<Foo::Bar *>(&obj);
+ QVERIFY(bar);
+ QCOMPARE(bar->rtti(), 42);
+ QCOMPARE(static_cast<Foo::Bar *>(&obj), bar);
+
+ BlehObject bleh;
+
+ bar = qobject_cast<Foo::Bar *>(&bleh);
+ QVERIFY(!bar);
+ Foo::Bleh *b = qobject_cast<Foo::Bleh *>(&bleh);
+ QCOMPARE(b->rtti(), 43);
+ QCOMPARE(static_cast<Foo::Bleh *>(&bleh), b);
+
+}
+
+class CustomData : public QObjectUserData
+{
+public:
+ int id;
+};
+
+void tst_QObject::testUserData()
+{
+ const int USER_DATA_COUNT = 100;
+ int user_data_ids[USER_DATA_COUNT];
+
+ // Register a few
+ for (int i=0; i<USER_DATA_COUNT; ++i) {
+ user_data_ids[i] = QObject::registerUserData();
+ }
+
+ // Randomize the table a bit
+ for (int i=0; i<100; ++i) {
+ int p1 = rand() % USER_DATA_COUNT;
+ int p2 = rand() % USER_DATA_COUNT;
+
+ int tmp = user_data_ids[p1];
+ user_data_ids[p1] = user_data_ids[p2];
+ user_data_ids[p2] = tmp;
+ }
+
+ // insert the user data into an object
+ QObject my_test_object;
+ for (int i=0; i<USER_DATA_COUNT; ++i) {
+ CustomData *data = new CustomData;
+ data->id = user_data_ids[i];
+ my_test_object.setUserData(data->id, data);
+ }
+
+ // verify that all ids and positions are matching
+ for (int i=0; i<USER_DATA_COUNT; ++i) {
+ int id = user_data_ids[i];
+ CustomData *data = static_cast<CustomData *>(my_test_object.userData(id));
+ QVERIFY(data != 0);
+ QVERIFY(data->id == id);
+ }
+}
+
+class DestroyedListener : public QObject
+{
+ Q_OBJECT
+public:
+ inline DestroyedListener() : pointerWasZero(false) {}
+
+ QPointer<QObject> pointer;
+ bool pointerWasZero;
+
+private slots:
+ inline void otherObjectDestroyed()
+ { pointerWasZero = pointer.isNull(); }
+};
+
+void tst_QObject::qpointerResetBeforeDestroyedSignal()
+{
+ QObject *obj = new QObject;
+ DestroyedListener listener;
+ listener.pointer = obj;
+ listener.pointerWasZero = false;
+ connect(obj, SIGNAL(destroyed()), &listener, SLOT(otherObjectDestroyed()));
+ delete obj;
+ QVERIFY(listener.pointerWasZero);
+ QVERIFY(listener.pointer.isNull());
+}
+
+class DefaultArguments : public QObject
+{
+ Q_OBJECT
+
+public slots:
+
+ void theSlot(const QString &s) { result = s; }
+
+signals:
+ void theOriginalSignal();
+ void theSecondSignal(const QString &s = QString("secondDefault"));
+
+public:
+
+ void emitTheOriginalSignal() { emit theOriginalSignal(); }
+ void emitTheSecondSignal() { emit theSecondSignal(); }
+ QString result;
+};
+
+void tst_QObject::connectSignalsToSignalsWithDefaultArguments()
+{
+ DefaultArguments o;
+ connect(&o, SIGNAL(theOriginalSignal()), &o, SIGNAL(theSecondSignal()));
+ connect(&o, SIGNAL(theSecondSignal(QString)), &o, SLOT(theSlot(QString)));
+ QVERIFY( o.result.isEmpty() );
+ o.emitTheSecondSignal();
+ QCOMPARE(o.result, QString("secondDefault"));
+ o.result = "Not called";
+ o.emitTheOriginalSignal();
+ QCOMPARE(o.result, QString("secondDefault"));
+
+}
+
+void tst_QObject::receivers()
+{
+ class Object : public QObject
+ {
+ public:
+ int receivers(const char* signal) const
+ { return QObject::receivers(signal); }
+ };
+
+ Object object;
+ QCOMPARE(object.receivers(SIGNAL(destroyed())), 0);
+ object.connect(&object, SIGNAL(destroyed()), SLOT(deleteLater()));
+ QCOMPARE(object.receivers(SIGNAL(destroyed())), 1);
+ object.connect(&object, SIGNAL(destroyed()), SLOT(deleteLater()));
+ QCOMPARE(object.receivers(SIGNAL(destroyed())), 2);
+ object.disconnect(SIGNAL(destroyed()), &object, SLOT(deleteLater()));
+ QCOMPARE(object.receivers(SIGNAL(destroyed())), 0);
+}
+
+enum Enum { };
+
+struct Struct { };
+class Class { };
+template <typename T> class Template { };
+
+class NormalizeObject : public QObject
+{
+ Q_OBJECT
+
+public:
+
+signals:
+ void uintPointerSignal(uint *);
+ void ulongPointerSignal(ulong *);
+ void constUintPointerSignal(const uint *);
+ void constUlongPointerSignal(const ulong *);
+
+ void structSignal(Struct s);
+ void classSignal(Class c);
+ void enumSignal(Enum e);
+
+ void structPointerSignal(Struct *s);
+ void classPointerSignal(Class *c);
+ void enumPointerSignal(Enum *e);
+
+ void constStructPointerSignal(const Struct *s);
+ void constClassPointerSignal(const Class *c);
+ void constEnumPointerSignal(const Enum *e);
+
+ void constStructPointerConstPointerSignal(const Struct * const *s);
+ void constClassPointerConstPointerSignal(const Class * const *c);
+ void constEnumPointerConstPointerSignal(const Enum * const *e);
+
+ void unsignedintSignal(unsigned int);
+ void unsignedSignal(unsigned);
+ void unsignedlongSignal(unsigned long);
+ void unsignedlonglongSignal(quint64);
+ void unsignedlongintSignal(unsigned long int);
+ void unsignedshortSignal(unsigned short);
+ void unsignedcharSignal(unsigned char);
+
+ void typeRefSignal(Template<Class &> &ref);
+ void constTypeRefSignal(const Template<Class const &> &ref);
+ void typeConstRefSignal(Template<Class const &> const &ref);
+
+ void typePointerConstRefSignal(Class * const &);
+
+ void constTemplateSignal1( Template<int > );
+ void constTemplateSignal2( Template< const int >);
+
+public slots:
+ void uintPointerSlot(uint *) { }
+ void ulongPointerSlot(ulong *) { }
+ void constUintPointerSlot(const uint *) { }
+ void constUlongPointerSlot(const ulong *) { }
+
+ void structSlot(Struct s) { Q_UNUSED(s); }
+ void classSlot(Class c) { Q_UNUSED(c); }
+ void enumSlot(Enum e) { Q_UNUSED(e); }
+
+ void structPointerSlot(Struct *s) { Q_UNUSED(s); }
+ void classPointerSlot(Class *c) { Q_UNUSED(c); }
+ void enumPointerSlot(Enum *e) { Q_UNUSED(e); }
+
+ void constStructPointerSlot(const Struct *s) { Q_UNUSED(s); }
+ void constClassPointerSlot(const Class *c) { Q_UNUSED(c); }
+ void constEnumPointerSlot(const Enum *e) { Q_UNUSED(e); }
+
+ void constStructPointerConstPointerSlot(const Struct * const *s) { Q_UNUSED(s); }
+ void constClassPointerConstPointerSlot(const Class * const *c) { Q_UNUSED(c); }
+ void constEnumPointerConstPointerSlot(const Enum * const *e) { Q_UNUSED(e); }
+
+ void uintSlot(uint) {};
+ void unsignedintSlot(unsigned int) {};
+ void unsignedSlot(unsigned) {};
+ void unsignedlongSlot(unsigned long) {};
+ void unsignedlonglongSlot(quint64) {};
+ void unsignedlongintSlot(unsigned long int) {};
+ void unsignedshortSlot(unsigned short) {};
+ void unsignedcharSlot(unsigned char) {};
+
+ void typeRefSlot(Template<Class &> &) {}
+ void constTypeRefSlot(const Template<const Class &> &) {}
+ void typeConstRefSlot(Template<Class const &> const &) {}
+
+ void typePointerConstRefSlot(Class * const &) {}
+
+ void constTemplateSlot1(Template<int > const) {}
+ void constTemplateSlot2(const Template<int > ) {}
+ void constTemplateSlot3(const Template< const int >) {}
+};
+
+#include "oldnormalizeobject.h"
+
+void tst_QObject::normalize()
+{
+ NormalizeObject object;
+
+ // unsigned int -> uint, unsigned long -> ulong
+ QVERIFY(object.connect(&object,
+ SIGNAL(uintPointerSignal(uint *)),
+ SLOT(uintPointerSlot(uint *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(uintPointerSignal(unsigned int *)),
+ SLOT(uintPointerSlot(uint *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(uintPointerSignal(uint *)),
+ SLOT(uintPointerSlot(unsigned int *))));
+
+ QVERIFY(object.connect(&object,
+ SIGNAL(constUintPointerSignal(const uint *)),
+ SLOT(constUintPointerSlot(const uint *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constUintPointerSignal(const unsigned int *)),
+ SLOT(constUintPointerSlot(const uint *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constUintPointerSignal(const uint *)),
+ SLOT(constUintPointerSlot(const unsigned int *))));
+
+ QVERIFY(object.connect(&object,
+ SIGNAL(ulongPointerSignal(ulong *)),
+ SLOT(ulongPointerSlot(ulong *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(ulongPointerSignal(unsigned long *)),
+ SLOT(ulongPointerSlot(ulong *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(ulongPointerSignal(ulong *)),
+ SLOT(ulongPointerSlot(unsigned long *))));
+
+ QVERIFY(object.connect(&object,
+ SIGNAL(constUlongPointerSignal(const ulong *)),
+ SLOT(constUlongPointerSlot(const ulong *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constUlongPointerSignal(const unsigned long *)),
+ SLOT(constUlongPointerSlot(const ulong *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constUlongPointerSignal(const ulong *)),
+ SLOT(constUlongPointerSlot(const unsigned long *))));
+
+ // struct, class, and enum are optional
+ QVERIFY(object.connect(&object,
+ SIGNAL(structSignal(struct Struct)),
+ SLOT(structSlot(struct Struct))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(structSignal(Struct)),
+ SLOT(structSlot(struct Struct))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(structSignal(struct Struct)),
+ SLOT(structSlot(Struct))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(classSignal(class Class)),
+ SLOT(classSlot(class Class))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(classSignal(Class)),
+ SLOT(classSlot(class Class))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(classSignal(class Class)),
+ SLOT(classSlot(Class))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(enumSignal(enum Enum)),
+ SLOT(enumSlot(enum Enum))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(enumSignal(Enum)),
+ SLOT(enumSlot(enum Enum))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(enumSignal(enum Enum)),
+ SLOT(enumSlot(Enum))));
+
+ QVERIFY(object.connect(&object,
+ SIGNAL(structPointerSignal(struct Struct *)),
+ SLOT(structPointerSlot(struct Struct *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(structPointerSignal(Struct *)),
+ SLOT(structPointerSlot(struct Struct *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(structPointerSignal(struct Struct *)),
+ SLOT(structPointerSlot(Struct *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(classPointerSignal(class Class *)),
+ SLOT(classPointerSlot(class Class *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(classPointerSignal(Class *)),
+ SLOT(classPointerSlot(class Class *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(classPointerSignal(class Class *)),
+ SLOT(classPointerSlot(Class *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(enumPointerSignal(enum Enum *)),
+ SLOT(enumPointerSlot(enum Enum *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(enumPointerSignal(Enum *)),
+ SLOT(enumPointerSlot(enum Enum *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(enumPointerSignal(enum Enum *)),
+ SLOT(enumPointerSlot(Enum *))));
+
+ QVERIFY(object.connect(&object,
+ SIGNAL(constStructPointerSignal(const struct Struct *)),
+ SLOT(constStructPointerSlot(const struct Struct *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constStructPointerSignal(const Struct *)),
+ SLOT(constStructPointerSlot(const struct Struct *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constStructPointerSignal(const struct Struct *)),
+ SLOT(constStructPointerSlot(const Struct *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constClassPointerSignal(const class Class *)),
+ SLOT(constClassPointerSlot(const class Class *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constClassPointerSignal(const Class *)),
+ SLOT(constClassPointerSlot(const class Class *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constClassPointerSignal(const class Class *)),
+ SLOT(constClassPointerSlot(const Class *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constEnumPointerSignal(const enum Enum *)),
+ SLOT(constEnumPointerSlot(const enum Enum *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constEnumPointerSignal(const Enum *)),
+ SLOT(constEnumPointerSlot(const enum Enum *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constEnumPointerSignal(const enum Enum *)),
+ SLOT(constEnumPointerSlot(const Enum *))));
+
+ QVERIFY(object.connect(&object,
+ SIGNAL(constStructPointerSignal(struct Struct const *)),
+ SLOT(constStructPointerSlot(struct Struct const *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constStructPointerSignal(Struct const *)),
+ SLOT(constStructPointerSlot(struct Struct const *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constStructPointerSignal(struct Struct const *)),
+ SLOT(constStructPointerSlot(Struct const *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constClassPointerSignal(class Class const *)),
+ SLOT(constClassPointerSlot(class Class const *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constClassPointerSignal(Class const *)),
+ SLOT(constClassPointerSlot(class Class const *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constClassPointerSignal(class Class const *)),
+ SLOT(constClassPointerSlot(Class const *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constEnumPointerSignal(enum Enum const *)),
+ SLOT(constEnumPointerSlot(enum Enum const *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constEnumPointerSignal(Enum const *)),
+ SLOT(constEnumPointerSlot(enum Enum const *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constEnumPointerSignal(enum Enum const *)),
+ SLOT(constEnumPointerSlot(Enum const *))));
+
+ QVERIFY(object.connect(&object,
+ SIGNAL(constStructPointerConstPointerSignal(const struct Struct * const *)),
+ SLOT(constStructPointerConstPointerSlot(const struct Struct * const *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constStructPointerConstPointerSignal(const Struct * const *)),
+ SLOT(constStructPointerConstPointerSlot(const struct Struct * const *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constStructPointerConstPointerSignal(const struct Struct * const *)),
+ SLOT(constStructPointerConstPointerSlot(const Struct * const *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constClassPointerConstPointerSignal(const class Class * const *)),
+ SLOT(constClassPointerConstPointerSlot(const class Class * const *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constClassPointerConstPointerSignal(const Class * const *)),
+ SLOT(constClassPointerConstPointerSlot(const class Class * const *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constClassPointerConstPointerSignal(const class Class * const *)),
+ SLOT(constClassPointerConstPointerSlot(const Class * const *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constEnumPointerConstPointerSignal(const enum Enum * const *)),
+ SLOT(constEnumPointerConstPointerSlot(const enum Enum * const *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constEnumPointerConstPointerSignal(const Enum * const *)),
+ SLOT(constEnumPointerConstPointerSlot(const enum Enum * const *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constEnumPointerConstPointerSignal(const enum Enum * const *)),
+ SLOT(constEnumPointerConstPointerSlot(const Enum * const *))));
+
+ QVERIFY(object.connect(&object,
+ SIGNAL(constStructPointerConstPointerSignal(struct Struct const * const *)),
+ SLOT(constStructPointerConstPointerSlot(struct Struct const * const *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constStructPointerConstPointerSignal(Struct const * const *)),
+ SLOT(constStructPointerConstPointerSlot(struct Struct const * const *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constStructPointerConstPointerSignal(struct Struct const * const *)),
+ SLOT(constStructPointerConstPointerSlot(Struct const * const *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constClassPointerConstPointerSignal(class Class const * const *)),
+ SLOT(constClassPointerConstPointerSlot(class Class const * const *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constClassPointerConstPointerSignal(Class const * const *)),
+ SLOT(constClassPointerConstPointerSlot(class Class const * const *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constClassPointerConstPointerSignal(class Class const * const *)),
+ SLOT(constClassPointerConstPointerSlot(Class const * const *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constEnumPointerConstPointerSignal(enum Enum const * const *)),
+ SLOT(constEnumPointerConstPointerSlot(enum Enum const * const *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constEnumPointerConstPointerSignal(Enum const * const *)),
+ SLOT(constEnumPointerConstPointerSlot(enum Enum const * const *))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constEnumPointerConstPointerSignal(enum Enum const * const *)),
+ SLOT(constEnumPointerConstPointerSlot(Enum const * const *))));
+
+ QVERIFY(object.connect(&object,
+ SIGNAL(unsignedintSignal(unsigned int)),
+ SLOT(unsignedintSlot(unsigned int))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(unsignedSignal(unsigned)),
+ SLOT(unsignedSlot(unsigned))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(unsignedSignal(unsigned)),
+ SLOT(uintSlot(uint))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(unsignedlongSignal(unsigned long)),
+ SLOT(unsignedlongSlot(unsigned long))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(unsignedlonglongSignal(quint64)),
+ SLOT(unsignedlonglongSlot(quint64))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(unsignedlongintSignal(unsigned long int)),
+ SLOT(unsignedlongintSlot(unsigned long int))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(unsignedshortSignal(unsigned short)),
+ SLOT(unsignedshortSlot(unsigned short))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(unsignedcharSignal(unsigned char)),
+ SLOT(unsignedcharSlot(unsigned char))));
+
+ // connect when original template signature and mixed usage of 'T<C const &> const &',
+ // 'const T<const C &> &', and 'T<const C &>'
+
+ QVERIFY(object.connect(&object,
+ SIGNAL(typeRefSignal(Template<Class &> &)),
+ SLOT(typeRefSlot(Template<Class &> &))));
+
+ QVERIFY(object.connect(&object,
+ SIGNAL(constTypeRefSignal(const Template<const Class &> &)),
+ SLOT(constTypeRefSlot(const Template<const Class &> &))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constTypeRefSignal(const Template<const Class &> &)),
+ SLOT(constTypeRefSlot(const Template<Class const &> &))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constTypeRefSignal(const Template<const Class &> &)),
+ SLOT(constTypeRefSlot(Template<Class const &> const &))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constTypeRefSignal(Template<const Class &> const &)),
+ SLOT(constTypeRefSlot(Template<Class const &> const &))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constTypeRefSignal(Template<Class const &> const &)),
+ SLOT(constTypeRefSlot(Template<Class const &> const &))));
+
+ QVERIFY(object.connect(&object,
+ SIGNAL(constTypeRefSignal(const Template<const Class &> &)),
+ SLOT(typeConstRefSlot(const Template<const Class &> &))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constTypeRefSignal(const Template<const Class &> &)),
+ SLOT(typeConstRefSlot(const Template<Class const &> &))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constTypeRefSignal(const Template<const Class &> &)),
+ SLOT(typeConstRefSlot(Template<Class const &> const &))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constTypeRefSignal(Template<const Class &> const &)),
+ SLOT(typeConstRefSlot(Template<Class const &> const &))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(constTypeRefSignal(Template<Class const &> const &)),
+ SLOT(typeConstRefSlot(Template<Class const &> const &))));
+
+ QVERIFY(object.connect(&object,
+ SIGNAL(typeConstRefSignal(const Template<const Class &> &)),
+ SLOT(constTypeRefSlot(const Template<const Class &> &))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(typeConstRefSignal(const Template<const Class &> &)),
+ SLOT(constTypeRefSlot(const Template<Class const &> &))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(typeConstRefSignal(const Template<const Class &> &)),
+ SLOT(constTypeRefSlot(Template<Class const &> const &))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(typeConstRefSignal(Template<const Class &> const &)),
+ SLOT(constTypeRefSlot(Template<Class const &> const &))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(typeConstRefSignal(Template<Class const &> const &)),
+ SLOT(constTypeRefSlot(Template<Class const &> const &))));
+
+ QVERIFY(object.connect(&object,
+ SIGNAL(typeConstRefSignal(const Template<const Class &> &)),
+ SLOT(typeConstRefSlot(const Template<const Class &> &))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(typeConstRefSignal(const Template<const Class &> &)),
+ SLOT(typeConstRefSlot(const Template<Class const &> &))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(typeConstRefSignal(const Template<const Class &> &)),
+ SLOT(typeConstRefSlot(Template<Class const &> const &))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(typeConstRefSignal(Template<const Class &> const &)),
+ SLOT(typeConstRefSlot(Template<Class const &> const &))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(typeConstRefSignal(Template<Class const &> const &)),
+ SLOT(typeConstRefSlot(Template<Class const &> const &))));
+
+ // same test again, this time with an object compiled with old moc output... we know that
+ // it is not possible to connect everything, whic is the purpose for this test
+ OldNormalizeObject oldobject;
+
+ QVERIFY(oldobject.connect(&oldobject,
+ SIGNAL(constTypeRefSignal(const Template<const Class &> &)),
+ SLOT(constTypeRefSlot(const Template<const Class &> &))));
+ QVERIFY(oldobject.connect(&oldobject,
+ SIGNAL(constTypeRefSignal(const Template<const Class &> &)),
+ SLOT(constTypeRefSlot(const Template<Class const &> &))));
+ // this fails in older versions, but passes now due to proper normalizing
+ QVERIFY(oldobject.connect(&oldobject,
+ SIGNAL(constTypeRefSignal(const Template<const Class &> &)),
+ SLOT(constTypeRefSlot(Template<Class const &> const &))));
+ // this fails in older versions, but passes now due to proper normalizing
+ QVERIFY(oldobject.connect(&oldobject,
+ SIGNAL(constTypeRefSignal(Template<const Class &> const &)),
+ SLOT(constTypeRefSlot(Template<Class const &> const &))));
+ // this fails in older versions, but passes now due to proper normalizing
+ QVERIFY(oldobject.connect(&oldobject,
+ SIGNAL(constTypeRefSignal(Template<Class const &> const &)),
+ SLOT(constTypeRefSlot(Template<Class const &> const &))));
+
+ // these fail in older Qt versions, but pass now due to proper normalizing
+ QVERIFY(oldobject.connect(&oldobject,
+ SIGNAL(constTypeRefSignal(const Template<const Class &> &)),
+ SLOT(typeConstRefSlot(const Template<const Class &> &))));
+ QVERIFY(oldobject.connect(&oldobject,
+ SIGNAL(constTypeRefSignal(const Template<const Class &> &)),
+ SLOT(typeConstRefSlot(const Template<Class const &> &))));
+ QVERIFY(oldobject.connect(&oldobject,
+ SIGNAL(constTypeRefSignal(const Template<const Class &> &)),
+ SLOT(typeConstRefSlot(Template<Class const &> const &))));
+ QVERIFY(oldobject.connect(&oldobject,
+ SIGNAL(constTypeRefSignal(Template<const Class &> const &)),
+ SLOT(typeConstRefSlot(Template<Class const &> const &))));
+ QVERIFY(oldobject.connect(&oldobject,
+ SIGNAL(constTypeRefSignal(Template<Class const &> const &)),
+ SLOT(typeConstRefSlot(Template<Class const &> const &))));
+
+ // these also fail in older Qt versions, but pass now due to proper normalizing
+ QVERIFY(oldobject.connect(&oldobject,
+ SIGNAL(typeConstRefSignal(const Template<const Class &> &)),
+ SLOT(constTypeRefSlot(const Template<const Class &> &))));
+ QVERIFY(oldobject.connect(&oldobject,
+ SIGNAL(typeConstRefSignal(const Template<const Class &> &)),
+ SLOT(constTypeRefSlot(const Template<Class const &> &))));
+ QVERIFY(oldobject.connect(&oldobject,
+ SIGNAL(typeConstRefSignal(const Template<const Class &> &)),
+ SLOT(constTypeRefSlot(Template<Class const &> const &))));
+ QVERIFY(oldobject.connect(&oldobject,
+ SIGNAL(typeConstRefSignal(Template<const Class &> const &)),
+ SLOT(constTypeRefSlot(Template<Class const &> const &))));
+ QVERIFY(oldobject.connect(&oldobject,
+ SIGNAL(typeConstRefSignal(Template<Class const &> const &)),
+ SLOT(constTypeRefSlot(Template<Class const &> const &))));
+
+ // this fails in older versions, but passes now due to proper normalizing
+ QVERIFY(oldobject.connect(&oldobject,
+ SIGNAL(typeConstRefSignal(const Template<const Class &> &)),
+ SLOT(typeConstRefSlot(const Template<const Class &> &))));
+ // this fails in older versions, but passes now due to proper normalizing
+ QVERIFY(oldobject.connect(&oldobject,
+ SIGNAL(typeConstRefSignal(const Template<const Class &> &)),
+ SLOT(typeConstRefSlot(const Template<Class const &> &))));
+ // this fails in older versions, but passes now due to proper normalizing
+ QVERIFY(oldobject.connect(&oldobject,
+ SIGNAL(typeConstRefSignal(const Template<const Class &> &)),
+ SLOT(typeConstRefSlot(Template<Class const &> const &))));
+ QVERIFY(oldobject.connect(&oldobject,
+ SIGNAL(typeConstRefSignal(Template<const Class &> const &)),
+ SLOT(typeConstRefSlot(Template<Class const &> const &))));
+ QVERIFY(oldobject.connect(&oldobject,
+ SIGNAL(typeConstRefSignal(Template<Class const &> const &)),
+ SLOT(typeConstRefSlot(Template<Class const &> const &))));
+
+ QVERIFY(object.connect(&object,
+ SIGNAL(typePointerConstRefSignal(Class*const&)),
+ SLOT(typePointerConstRefSlot(Class*const&))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(typePointerConstRefSignal(Class*const&)),
+ SLOT(typePointerConstRefSlot(Class*))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(typePointerConstRefSignal(Class*)),
+ SLOT(typePointerConstRefSlot(Class*const&))));
+ QVERIFY(object.connect(&object,
+ SIGNAL(typePointerConstRefSignal(Class*)),
+ SLOT(typePointerConstRefSlot(Class*))));
+
+ QVERIFY( connect(&object, SIGNAL(constTemplateSignal1(Template <int>)),
+ &object , SLOT(constTemplateSlot1 (Template<int > ) ) ));
+ QVERIFY( connect(&object, SIGNAL(constTemplateSignal1(Template <int>)),
+ &object , SLOT(constTemplateSlot2 (Template<int > ) ) ));
+ QVERIFY( connect(&object, SIGNAL(constTemplateSignal2(Template <const int>)),
+ &object , SLOT(constTemplateSlot3(Template<int const > ) ) ));
+
+ //type does not match
+ QTest::ignoreMessage(QtWarningMsg, "QObject::connect: Incompatible sender/receiver arguments\n"
+ " NormalizeObject::constTemplateSignal1(Template<int>) --> NormalizeObject::constTemplateSlot3(Template<const int>)");
+ QVERIFY(!connect(&object, SIGNAL(constTemplateSignal1(Template <int>)),
+ &object , SLOT(constTemplateSlot3(Template<int const> ) ) ));
+}
+
+class SiblingDeleter : public QObject
+{
+public:
+ inline SiblingDeleter(QObject *sibling, QObject *parent)
+ : QObject(parent), sibling(sibling) {}
+ inline virtual ~SiblingDeleter() { delete sibling; }
+
+private:
+ QPointer<QObject> sibling;
+};
+
+
+void tst_QObject::childDeletesItsSibling()
+{
+ QObject *commonParent = new QObject(0);
+ QPointer<QObject> child = new QObject(0);
+ QPointer<QObject> siblingDeleter = new SiblingDeleter(child, commonParent);
+ child->setParent(commonParent);
+ delete commonParent; // don't crash
+ QVERIFY(!child);
+ QVERIFY(!siblingDeleter);
+}
+
+void tst_QObject::floatProperty()
+{
+ PropertyObject obj;
+ const int idx = obj.metaObject()->indexOfProperty("myFloat");
+ QVERIFY(idx > 0);
+ QMetaProperty prop = obj.metaObject()->property(idx);
+ QVERIFY(prop.isValid());
+ QVERIFY(prop.type() == uint(QMetaType::type("float")));
+ QVERIFY(!prop.write(&obj, QVariant("Hello")));
+ QVERIFY(prop.write(&obj, qVariantFromValue(128.0f)));
+ QVariant v = prop.read(&obj);
+ QVERIFY(int(v.userType()) == QMetaType::Float);
+ QVERIFY(qVariantValue<float>(v) == 128.0f);
+}
+
+void tst_QObject::qrealProperty()
+{
+ PropertyObject obj;
+ const int idx = obj.metaObject()->indexOfProperty("myQReal");
+ QVERIFY(idx > 0);
+ QMetaProperty prop = obj.metaObject()->property(idx);
+ QVERIFY(prop.isValid());
+ QVERIFY(prop.type() == uint(QMetaType::type("qreal")));
+ QVERIFY(!prop.write(&obj, QVariant("Hello")));
+
+ QVERIFY(prop.write(&obj, qVariantFromValue(128.0f)));
+ QVariant v = prop.read(&obj);
+ QCOMPARE(v.userType(), qMetaTypeId<qreal>());
+ QVERIFY(qVariantValue<qreal>(v) == 128.0);
+
+ QVERIFY(prop.write(&obj, qVariantFromValue(double(127))));
+ v = prop.read(&obj);
+ QCOMPARE(v.userType(), qMetaTypeId<qreal>());
+ QVERIFY(qVariantValue<qreal>(v) == 127.0);
+}
+
+class DynamicPropertyObject : public PropertyObject
+{
+public:
+ inline DynamicPropertyObject() {}
+
+ inline virtual bool event(QEvent *e) {
+ if (e->type() == QEvent::DynamicPropertyChange) {
+ changedDynamicProperties.append(static_cast<QDynamicPropertyChangeEvent *>(e)->propertyName());
+ }
+ return QObject::event(e);
+ }
+
+ QList<QByteArray> changedDynamicProperties;
+};
+
+void tst_QObject::dynamicProperties()
+{
+ DynamicPropertyObject obj;
+
+ QVERIFY(obj.dynamicPropertyNames().isEmpty());
+
+ QVERIFY(obj.setProperty("number", 42));
+ QVERIFY(obj.changedDynamicProperties.isEmpty());
+ QCOMPARE(obj.property("number").toInt(), 42);
+
+ QVERIFY(!obj.setProperty("number", "invalid string"));
+ QVERIFY(obj.changedDynamicProperties.isEmpty());
+
+ QVERIFY(!obj.setProperty("myuserproperty", "Hello"));
+ QCOMPARE(obj.changedDynamicProperties.count(), 1);
+ QCOMPARE(obj.changedDynamicProperties.first(), QByteArray("myuserproperty"));
+ obj.changedDynamicProperties.clear();
+
+ QCOMPARE(obj.property("myuserproperty").toString(), QString("Hello"));
+
+ QCOMPARE(obj.dynamicPropertyNames().count(), 1);
+ QCOMPARE(obj.dynamicPropertyNames().first(), QByteArray("myuserproperty"));
+
+ QVERIFY(!obj.setProperty("myuserproperty", QVariant()));
+
+ QCOMPARE(obj.changedDynamicProperties.count(), 1);
+ QCOMPARE(obj.changedDynamicProperties.first(), QByteArray("myuserproperty"));
+ obj.changedDynamicProperties.clear();
+
+ QVERIFY(obj.property("myuserproperty").isNull());
+
+ QVERIFY(obj.dynamicPropertyNames().isEmpty());
+}
+
+void tst_QObject::recursiveSignalEmission()
+{
+#if defined(Q_OS_SYMBIAN) && defined(Q_CC_NOKIAX86)
+ QSKIP("Emulator builds in Symbian do not support launching processes linking to Qt", SkipAll);
+#elif defined(QT_NO_PROCESS)
+ QSKIP("Test requires QProcess", SkipAll);
+#else
+ QProcess proc;
+ proc.start("./signalbug");
+ QVERIFY(proc.waitForFinished());
+ QVERIFY(proc.exitStatus() == QProcess::NormalExit);
+ QCOMPARE(proc.exitCode(), 0);
+#endif
+}
+
+void tst_QObject::blockingQueuedConnection()
+{
+ {
+ SenderObject sender;
+
+ MoveToThreadThread thread;
+ ReceiverObject receiver;
+ receiver.moveToThread(&thread);
+ thread.start();
+
+ receiver.connect(&sender, SIGNAL(signal1()), SLOT(slot1()), Qt::BlockingQueuedConnection);
+ sender.emitSignal1();
+ QVERIFY(receiver.called(1));
+
+ receiver.reset();
+ QVERIFY(QMetaObject::invokeMethod(&receiver, "slot1", Qt::BlockingQueuedConnection));
+ QVERIFY(receiver.called(1));
+
+ thread.quit();
+ QVERIFY(thread.wait());
+ }
+}
+
+class EventSpy : public QObject
+{
+ Q_OBJECT
+
+public:
+ typedef QList<QPair<QObject *, QEvent::Type> > EventList;
+
+ EventSpy(QObject *parent = 0)
+ : QObject(parent)
+ { }
+
+ EventList eventList()
+ {
+ return events;
+ }
+
+ void clear()
+ {
+ events.clear();
+ }
+
+ bool eventFilter(QObject *object, QEvent *event)
+ {
+ events.append(qMakePair(object, event->type()));
+ return false;
+ }
+
+private:
+ EventList events;
+};
+
+void tst_QObject::compatibilityChildInsertedEvents()
+{
+ EventSpy::EventList expected;
+
+ {
+ // no children created, so we expect no events
+ QObject object;
+ EventSpy spy;
+ object.installEventFilter(&spy);
+
+ QCoreApplication::postEvent(&object, new QEvent(QEvent::Type(QEvent::User + 1)));
+
+ QCoreApplication::processEvents();
+
+ expected =
+ EventSpy::EventList()
+ << qMakePair(&object, QEvent::Type(QEvent::User + 1));
+ QCOMPARE(spy.eventList(), expected);
+ }
+
+ {
+ // 2 children, so we expect 2 ChildAdded and 2 ChildInserted events
+ QObject object;
+ EventSpy spy;
+ object.installEventFilter(&spy);
+
+ QCoreApplication::postEvent(&object, new QEvent(QEvent::Type(QEvent::User + 1)));
+
+ QObject child1(&object);
+ QObject child2;
+ child2.setParent(&object);
+
+ QCoreApplication::postEvent(&object, new QEvent(QEvent::Type(QEvent::User + 2)));
+
+ expected =
+ EventSpy::EventList()
+ << qMakePair(&object, QEvent::ChildAdded)
+ << qMakePair(&object, QEvent::ChildAdded);
+ QCOMPARE(spy.eventList(), expected);
+ spy.clear();
+
+ QCoreApplication::processEvents();
+
+ expected =
+ EventSpy::EventList()
+ << qMakePair(&object, QEvent::Type(QEvent::User + 1))
+ << qMakePair(&object, QEvent::Type(QEvent::User + 2));
+ QCOMPARE(spy.eventList(), expected);
+ }
+
+ {
+ // 2 children, but one is reparented away, so we expect:
+ // 2 ChildAdded, 1 ChildRemoved, and 1 ChildInserted
+ QObject object;
+ EventSpy spy;
+ object.installEventFilter(&spy);
+
+ QCoreApplication::postEvent(&object, new QEvent(QEvent::Type(QEvent::User + 1)));
+
+ QObject child1(&object);
+ QObject child2;
+ child2.setParent(&object);
+ child2.setParent(0);
+
+ QCoreApplication::postEvent(&object, new QEvent(QEvent::Type(QEvent::User + 2)));
+
+ expected =
+ EventSpy::EventList()
+ << qMakePair(&object, QEvent::ChildAdded)
+ << qMakePair(&object, QEvent::ChildAdded)
+ << qMakePair(&object, QEvent::ChildRemoved);
+ QCOMPARE(spy.eventList(), expected);
+ spy.clear();
+
+ QCoreApplication::processEvents();
+
+ expected =
+ EventSpy::EventList()
+ << qMakePair(&object, QEvent::Type(QEvent::User + 1))
+ << qMakePair(&object, QEvent::Type(QEvent::User + 2));
+ QCOMPARE(spy.eventList(), expected);
+ }
+}
+
+void tst_QObject::installEventFilter()
+{
+ QEvent event(QEvent::User);
+ EventSpy::EventList expected;
+
+ QObject object;
+ EventSpy spy;
+ object.installEventFilter(&spy);
+
+ // nothing special, should just work
+ QCoreApplication::sendEvent(&object, &event);
+ expected =
+ EventSpy::EventList()
+ << qMakePair(&object, QEvent::User);
+ QCOMPARE(spy.eventList(), expected);
+ spy.clear();
+
+ // moving the filter causes QCoreApplication to skip the filter
+ spy.moveToThread(0);
+ QTest::ignoreMessage(QtWarningMsg, "QCoreApplication: Object event filter cannot be in a different thread.");
+ QCoreApplication::sendEvent(&object, &event);
+ QVERIFY(spy.eventList().isEmpty());
+
+ // move it back, and the filter works again
+ spy.moveToThread(object.thread());
+ QCoreApplication::sendEvent(&object, &event);
+ expected =
+ EventSpy::EventList()
+ << qMakePair(&object, QEvent::User);
+ QCOMPARE(spy.eventList(), expected);
+ spy.clear();
+
+ // cannot install an event filter that lives in a different thread
+ object.removeEventFilter(&spy);
+ spy.moveToThread(0);
+ QTest::ignoreMessage(QtWarningMsg, "QObject::installEventFilter(): Cannot filter events for objects in a different thread.");
+ object.installEventFilter(&spy);
+ QCoreApplication::sendEvent(&object, &event);
+ QVERIFY(spy.eventList().isEmpty());
+}
+
+class EmitThread : public QThread
+{ Q_OBJECT
+public:
+ void run(void) {
+ emit work();
+ }
+signals:
+ void work();
+};
+
+namespace QObjectTest { // Do not clash with WinAPI 'DeleteObject'
+class DeleteObject : public QObject
+{
+ Q_OBJECT
+
+public slots:
+ void deleteSelf()
+ {
+ delete this;
+ }
+
+ void relaySignalAndProcessEvents()
+ {
+ emit relayedSignal();
+ QCoreApplication::processEvents();
+ }
+
+signals:
+ void relayedSignal();
+};
+} // namespace QObjectTest
+
+void tst_QObject::deleteSelfInSlot()
+{
+ {
+ SenderObject sender;
+ QObjectTest::DeleteObject *receiver = new QObjectTest::DeleteObject();
+ receiver->connect(&sender,
+ SIGNAL(signal1()),
+ SLOT(deleteSelf()),
+ Qt::BlockingQueuedConnection);
+
+ QThread thread;
+ receiver->moveToThread(&thread);
+ thread.connect(receiver, SIGNAL(destroyed()), SLOT(quit()), Qt::DirectConnection);
+ thread.start();
+
+ QPointer<QObjectTest::DeleteObject> p = receiver;
+ sender.emitSignal1();
+ QVERIFY(p.isNull());
+
+ QVERIFY(thread.wait(10000));
+ }
+
+ {
+ SenderObject sender;
+ QObjectTest::DeleteObject *receiver = new QObjectTest::DeleteObject();
+ receiver->connect(&sender,
+ SIGNAL(signal1()),
+ SLOT(relaySignalAndProcessEvents()),
+ Qt::BlockingQueuedConnection);
+ receiver->connect(receiver,
+ SIGNAL(relayedSignal()),
+ SLOT(deleteSelf()),
+ Qt::QueuedConnection);
+
+ QThread thread;
+ receiver->moveToThread(&thread);
+ thread.connect(receiver, SIGNAL(destroyed()), SLOT(quit()), Qt::DirectConnection);
+ thread.start();
+
+ QPointer<QObjectTest::DeleteObject> p = receiver;
+ sender.emitSignal1();
+ QVERIFY(p.isNull());
+
+ QVERIFY(thread.wait(10000));
+ }
+
+ {
+ EmitThread sender;
+ QObjectTest::DeleteObject *receiver = new QObjectTest::DeleteObject();
+ connect(&sender, SIGNAL(work()), receiver, SLOT(deleteSelf()), Qt::DirectConnection);
+ QPointer<QObjectTest::DeleteObject> p = receiver;
+ sender.start();
+ QVERIFY(sender.wait(10000));
+ QVERIFY(p.isNull());
+ }
+}
+
+class DisconnectObject : public QObject
+{
+ Q_OBJECT
+
+public slots:
+ void disconnectSelf()
+ {
+ disconnect(sender(), 0, this, 0);
+ }
+
+ void relaySignalAndProcessEvents()
+ {
+ emit relayedSignal();
+ QCoreApplication::processEvents();
+ }
+
+signals:
+ void relayedSignal();
+};
+
+void tst_QObject::disconnectSelfInSlotAndDeleteAfterEmit()
+{
+ {
+ SenderObject sender;
+ DisconnectObject *receiver = new DisconnectObject();
+ receiver->connect(&sender, SIGNAL(signal1()), SLOT(disconnectSelf()));
+ sender.emitSignal1AfterRecursion();
+ delete receiver;
+ }
+
+ {
+ SenderObject sender;
+ DisconnectObject *receiver = new DisconnectObject();
+ receiver->connect(&sender,
+ SIGNAL(signal1()),
+ SLOT(disconnectSelf()),
+ Qt::BlockingQueuedConnection);
+
+ QThread thread;
+ receiver->moveToThread(&thread);
+ thread.connect(receiver, SIGNAL(destroyed()), SLOT(quit()), Qt::DirectConnection);
+ thread.start();
+
+ QPointer<DisconnectObject> p = receiver;
+ sender.emitSignal1();
+ QVERIFY(!p.isNull());
+
+ receiver->deleteLater();
+
+ QVERIFY(thread.wait(10000));
+ QVERIFY(p.isNull());
+ }
+
+ {
+ SenderObject sender;
+ DisconnectObject *receiver = new DisconnectObject();
+ receiver->connect(&sender,
+ SIGNAL(signal1()),
+ SLOT(relaySignalAndProcessEvents()),
+ Qt::BlockingQueuedConnection);
+ receiver->connect(receiver,
+ SIGNAL(relayedSignal()),
+ SLOT(disconnectSelf()),
+ Qt::QueuedConnection);
+
+ QThread thread;
+ receiver->moveToThread(&thread);
+ thread.connect(receiver, SIGNAL(destroyed()), SLOT(quit()), Qt::DirectConnection);
+ thread.start();
+
+ QPointer<DisconnectObject> p = receiver;
+ sender.emitSignal1();
+ QVERIFY(!p.isNull());
+
+ receiver->deleteLater();
+
+ QVERIFY(thread.wait(10000));
+ QVERIFY(p.isNull());
+ }
+}
+
+void tst_QObject::dumpObjectInfo()
+{
+ QObject a, b;
+ QObject::connect(&a, SIGNAL(destroyed(QObject *)), &b, SLOT(deleteLater()));
+ a.disconnect(&b);
+ a.dumpObjectInfo(); // should not crash
+}
+
+class ConnectToSender : public QObject
+{ Q_OBJECT
+ public slots:
+ void uselessSlot() { count++; }
+
+ void harmfullSlot() {
+ //this used to crash
+ connect(sender(), SIGNAL(signal4()), this, SLOT(uselessSlot()));
+ //play a little bit with the memory in order to really get a segfault.
+ connect(sender(), SIGNAL(signal1()), this, SLOT(uselessSlot()));
+ QList<double>() << 45 << 78 << 65 << 121 << 45 << 78 << 12;
+ }
+ public:
+ int count;
+};
+
+void tst_QObject::connectToSender()
+{
+ SenderObject s;
+ ConnectToSender r;
+ r.count = 0;
+ QObject::connect(&s, SIGNAL(signal1()), &r, SLOT(harmfullSlot()));
+ QObject::connect(&s, SIGNAL(signal1()), &r, SLOT(uselessSlot()));
+
+ s.emitSignal1();
+
+ QCOMPARE(r.count, 1);
+ s.emitSignal4();
+ QCOMPARE(r.count, 2);
+}
+
+void tst_QObject::qobjectConstCast()
+{
+ FooObject obj;
+
+ QObject *ptr = &obj;
+ const QObject *cptr = &obj;
+
+ QVERIFY(qobject_cast<FooObject *>(ptr));
+ QVERIFY(qobject_cast<const FooObject *>(cptr));
+}
+
+void tst_QObject::uniqConnection()
+{
+ SenderObject *s = new SenderObject;
+ ReceiverObject *r1 = new ReceiverObject;
+ ReceiverObject *r2 = new ReceiverObject;
+ r1->reset();
+ r2->reset();
+ ReceiverObject::sequence = 0;
+
+ QVERIFY( connect( s, SIGNAL( signal1() ), r1, SLOT( slot1() ) , Qt::UniqueConnection) );
+ QVERIFY( connect( s, SIGNAL( signal1() ), r2, SLOT( slot1() ) , Qt::UniqueConnection) );
+ QVERIFY( connect( s, SIGNAL( signal1() ), r1, SLOT( slot3() ) , Qt::UniqueConnection) );
+ QVERIFY( connect( s, SIGNAL( signal3() ), r1, SLOT( slot3() ) , Qt::UniqueConnection) );
+
+ s->emitSignal1();
+ s->emitSignal2();
+ s->emitSignal3();
+ s->emitSignal4();
+
+ QCOMPARE( r1->count_slot1, 1 );
+ QCOMPARE( r1->count_slot2, 0 );
+ QCOMPARE( r1->count_slot3, 2 );
+ QCOMPARE( r1->count_slot4, 0 );
+ QCOMPARE( r2->count_slot1, 1 );
+ QCOMPARE( r2->count_slot2, 0 );
+ QCOMPARE( r2->count_slot3, 0 );
+ QCOMPARE( r2->count_slot4, 0 );
+ QCOMPARE( r1->sequence_slot1, 1 );
+ QCOMPARE( r2->sequence_slot1, 2 );
+ QCOMPARE( r1->sequence_slot3, 4 );
+
+ r1->reset();
+ r2->reset();
+ ReceiverObject::sequence = 0;
+
+ QVERIFY( connect( s, SIGNAL( signal4() ), r1, SLOT( slot4() ) , Qt::UniqueConnection) );
+ QVERIFY( connect( s, SIGNAL( signal4() ), r2, SLOT( slot4() ) , Qt::UniqueConnection) );
+ QVERIFY(!connect( s, SIGNAL( signal4() ), r2, SLOT( slot4() ) , Qt::UniqueConnection) );
+ QVERIFY( connect( s, SIGNAL( signal1() ), r2, SLOT( slot4() ) , Qt::UniqueConnection) );
+ QVERIFY(!connect( s, SIGNAL( signal4() ), r1, SLOT( slot4() ) , Qt::UniqueConnection) );
+
+ s->emitSignal4();
+ QCOMPARE( r1->count_slot4, 1 );
+ QCOMPARE( r2->count_slot4, 1 );
+ QCOMPARE( r1->sequence_slot4, 1 );
+ QCOMPARE( r2->sequence_slot4, 2 );
+
+ r1->reset();
+ r2->reset();
+ ReceiverObject::sequence = 0;
+
+ connect( s, SIGNAL( signal4() ), r1, SLOT( slot4() ) );
+
+ s->emitSignal4();
+ QCOMPARE( r1->count_slot4, 2 );
+ QCOMPARE( r2->count_slot4, 1 );
+ QCOMPARE( r1->sequence_slot4, 3 );
+ QCOMPARE( r2->sequence_slot4, 2 );
+
+ delete s;
+ delete r1;
+ delete r2;
+}
+
+void tst_QObject::interfaceIid()
+{
+ QCOMPARE(QByteArray(qobject_interface_iid<Foo::Bleh *>()),
+ QByteArray(Bleh_iid));
+ QCOMPARE(QByteArray(qobject_interface_iid<Foo::Bar *>()),
+ QByteArray("com.qtest.foobar"));
+ QCOMPARE(QByteArray(qobject_interface_iid<FooObject *>()),
+ QByteArray());
+}
+
+void tst_QObject::deleteQObjectWhenDeletingEvent()
+{
+ //this is related to task 259514
+ //before the fix this used to dead lock when the QObject from the event was destroyed
+
+ struct MyEvent : public QEvent
+ {
+ MyEvent() : QEvent(QEvent::User) { }
+ QObject obj;
+ };
+
+ QObject o;
+ QApplication::postEvent(&o, new MyEvent);
+ QCoreApplication::removePostedEvents(&o); // here you would get a deadlock
+}
+
+class OverloadObject : public QObject
+{
+ friend class tst_QObject;
+ Q_OBJECT
+ signals:
+ void sig(int i, char c, qreal m = 12);
+ void sig(int i, int j = 12);
+ void sig(QObject *o, QObject *p, QObject *q = 0, QObject *r = 0) const;
+ void other(int a = 0);
+ void sig(QObject *o, OverloadObject *p = 0, QObject *q = 0, QObject *r = 0);
+ void sig(double r = 0.5);
+ public slots:
+ void slo(int i, int j = 43)
+ {
+ s_num += 1;
+ i1_num = i;
+ i2_num = j;
+ }
+ void slo(QObject *o, QObject *p = qApp, QObject *q = qApp, QObject *r = qApp)
+ {
+ s_num += 10;
+ o1_obj = o;
+ o2_obj = p;
+ o3_obj = q;
+ o4_obj = r;
+ }
+ void slo()
+ {
+ s_num += 100;
+ }
+
+ public:
+ int s_num;
+ int i1_num;
+ int i2_num;
+ QObject *o1_obj;
+ QObject *o2_obj;
+ QObject *o3_obj;
+ QObject *o4_obj;
+};
+
+void tst_QObject::overloads()
+{
+ OverloadObject obj1;
+ OverloadObject obj2;
+ QObject obj3;
+ obj1.s_num = 0;
+ obj2.s_num = 0;
+
+ connect (&obj1, SIGNAL(sig(int)) , &obj1, SLOT(slo(int)));
+ connect (&obj1, SIGNAL(sig(QObject *, QObject *, QObject *)) , &obj1, SLOT(slo(QObject * , QObject *, QObject *)));
+
+ connect (&obj1, SIGNAL(sig(QObject *, QObject *, QObject *, QObject *)) , &obj2, SLOT(slo(QObject * , QObject *, QObject *)));
+ connect (&obj1, SIGNAL(sig(QObject *)) , &obj2, SLOT(slo()));
+ connect (&obj1, SIGNAL(sig(int, int)) , &obj2, SLOT(slo(int, int)));
+
+ emit obj1.sig(0.5); //connected to nothing
+ emit obj1.sig(1, 'a'); //connected to nothing
+ QCOMPARE(obj1.s_num, 0);
+ QCOMPARE(obj2.s_num, 0);
+
+ emit obj1.sig(1); //this signal is connected
+ QCOMPARE(obj1.s_num, 1);
+ QCOMPARE(obj1.i1_num, 1);
+ QCOMPARE(obj1.i2_num, 43); //default argument of the slot
+
+ QCOMPARE(obj2.s_num, 1);
+ QCOMPARE(obj2.i1_num, 1);
+ QCOMPARE(obj2.i2_num, 12); //default argument of the signal
+
+
+ emit obj1.sig(&obj2); //this signal is conencted to obj2
+ QCOMPARE(obj1.s_num, 1);
+ QCOMPARE(obj2.s_num, 101);
+ emit obj1.sig(&obj2, &obj3); //this signal is connected
+ QCOMPARE(obj1.s_num, 11);
+ QCOMPARE(obj1.o1_obj, (QObject *)&obj2);
+ QCOMPARE(obj1.o2_obj, &obj3);
+ QCOMPARE(obj1.o3_obj, (QObject *)0); //default arg of the signal
+ QCOMPARE(obj1.o4_obj, (QObject *)qApp); //default arg of the slot
+
+ QCOMPARE(obj2.s_num, 111);
+ QCOMPARE(obj2.o1_obj, (QObject *)&obj2);
+ QCOMPARE(obj2.o2_obj, &obj3);
+ QCOMPARE(obj2.o3_obj, (QObject *)0); //default arg of the signal
+ QCOMPARE(obj2.o4_obj, (QObject *)qApp); //default arg of the slot
+}
+
+class ManySignals : public QObject
+{ Q_OBJECT
+ friend class tst_QObject;
+signals:
+ void sig00(); void sig01(); void sig02(); void sig03(); void sig04();
+ void sig05(); void sig06(); void sig07(); void sig08(); void sig09();
+ void sig10(); void sig11(); void sig12(); void sig13(); void sig14();
+ void sig15(); void sig16(); void sig17(); void sig18(); void sig19();
+ void sig20(); void sig21(); void sig22(); void sig23(); void sig24();
+ void sig25(); void sig26(); void sig27(); void sig28(); void sig29();
+ void sig30(); void sig31(); void sig32(); void sig33(); void sig34();
+ void sig35(); void sig36(); void sig37(); void sig38(); void sig39();
+ void sig40(); void sig41(); void sig42(); void sig43(); void sig44();
+ void sig45(); void sig46(); void sig47(); void sig48(); void sig49();
+ void sig50(); void sig51(); void sig52(); void sig53(); void sig54();
+ void sig55(); void sig56(); void sig57(); void sig58(); void sig59();
+ void sig60(); void sig61(); void sig62(); void sig63(); void sig64();
+ void sig65(); void sig66(); void sig67(); void sig68(); void sig69();
+
+public slots:
+ void received() { rec++; }
+public:
+ int rec;
+};
+
+
+void tst_QObject::isSignalConnected()
+{
+ ManySignals o;
+ o.rec = 0;
+#ifdef QT_BUILD_INTERNAL
+ QObjectPrivate *priv = QObjectPrivate::get(&o);
+ QVERIFY(!priv->isSignalConnected(priv->signalIndex("destroyed()")));
+ QVERIFY(!priv->isSignalConnected(priv->signalIndex("sig00()")));
+ QVERIFY(!priv->isSignalConnected(priv->signalIndex("sig05()")));
+ QVERIFY(!priv->isSignalConnected(priv->signalIndex("sig15()")));
+ QVERIFY(!priv->isSignalConnected(priv->signalIndex("sig29()")));
+ QVERIFY(!priv->isSignalConnected(priv->signalIndex("sig60()")));
+ QVERIFY(!priv->isSignalConnected(priv->signalIndex("sig61()")));
+#endif
+
+ QObject::connect(&o, SIGNAL(sig00()), &o, SIGNAL(sig69()));
+ QObject::connect(&o, SIGNAL(sig34()), &o, SIGNAL(sig03()));
+ QObject::connect(&o, SIGNAL(sig69()), &o, SIGNAL(sig34()));
+ QObject::connect(&o, SIGNAL(sig03()), &o, SIGNAL(sig18()));
+
+#ifdef QT_BUILD_INTERNAL
+ QVERIFY(!priv->isSignalConnected(priv->signalIndex("destroyed()")));
+ QVERIFY(!priv->isSignalConnected(priv->signalIndex("sig05()")));
+ QVERIFY(!priv->isSignalConnected(priv->signalIndex("sig15()")));
+ QVERIFY(!priv->isSignalConnected(priv->signalIndex("sig29()")));
+
+ QVERIFY(priv->isSignalConnected(priv->signalIndex("sig00()")));
+ QVERIFY(priv->isSignalConnected(priv->signalIndex("sig03()")));
+ QVERIFY(priv->isSignalConnected(priv->signalIndex("sig34()")));
+ QVERIFY(priv->isSignalConnected(priv->signalIndex("sig69()")));
+ QVERIFY(!priv->isSignalConnected(priv->signalIndex("sig18()")));
+#endif
+
+ QObject::connect(&o, SIGNAL(sig18()), &o, SIGNAL(sig29()));
+ QObject::connect(&o, SIGNAL(sig29()), &o, SIGNAL(sig62()));
+ QObject::connect(&o, SIGNAL(sig62()), &o, SIGNAL(sig28()));
+ QObject::connect(&o, SIGNAL(sig28()), &o, SIGNAL(sig27()));
+
+#ifdef QT_BUILD_INTERNAL
+ QVERIFY(priv->isSignalConnected(priv->signalIndex("sig18()")));
+ QVERIFY(priv->isSignalConnected(priv->signalIndex("sig62()")));
+ QVERIFY(priv->isSignalConnected(priv->signalIndex("sig28()")));
+ QVERIFY(priv->isSignalConnected(priv->signalIndex("sig69()")));
+ QVERIFY(!priv->isSignalConnected(priv->signalIndex("sig27()")));
+#endif
+
+ QCOMPARE(o.rec, 0);
+ emit o.sig01();
+ emit o.sig34();
+ QCOMPARE(o.rec, 0);
+
+ QObject::connect(&o, SIGNAL(sig27()), &o, SLOT(received()));
+
+#ifdef QT_BUILD_INTERNAL
+ QVERIFY(priv->isSignalConnected(priv->signalIndex("sig00()")));
+ QVERIFY(priv->isSignalConnected(priv->signalIndex("sig03()")));
+ QVERIFY(priv->isSignalConnected(priv->signalIndex("sig34()")));
+ QVERIFY(priv->isSignalConnected(priv->signalIndex("sig18()")));
+ QVERIFY(priv->isSignalConnected(priv->signalIndex("sig62()")));
+ QVERIFY(priv->isSignalConnected(priv->signalIndex("sig28()")));
+ QVERIFY(priv->isSignalConnected(priv->signalIndex("sig69()")));
+ QVERIFY(priv->isSignalConnected(priv->signalIndex("sig27()")));
+
+ QVERIFY(!priv->isSignalConnected(priv->signalIndex("sig04()")));
+ QVERIFY(!priv->isSignalConnected(priv->signalIndex("sig21()")));
+ QVERIFY(!priv->isSignalConnected(priv->signalIndex("sig25()")));
+ QVERIFY(!priv->isSignalConnected(priv->signalIndex("sig55()")));
+ QVERIFY(!priv->isSignalConnected(priv->signalIndex("sig61()")));
+#endif
+
+ emit o.sig00();
+ QCOMPARE(o.rec, 1);
+ emit o.sig69();
+ QCOMPARE(o.rec, 2);
+ emit o.sig36();
+ QCOMPARE(o.rec, 2);
+}
+
+void tst_QObject::qMetaObjectConnect()
+{
+ SenderObject *s = new SenderObject;
+ ReceiverObject *r1 = new ReceiverObject;
+ ReceiverObject *r2 = new ReceiverObject;
+ r1->reset();
+ r2->reset();
+ ReceiverObject::sequence = 0;
+
+ int signal1Index = s->metaObject()->indexOfSignal("signal1()");
+ int signal3Index = s->metaObject()->indexOfSignal("signal3()");
+ int slot1Index = r1->metaObject()->indexOfSlot("slot1()");
+ int slot2Index = r1->metaObject()->indexOfSlot("slot2()");
+ int slot3Index = r1->metaObject()->indexOfSlot("slot3()");
+
+ QVERIFY(slot1Index > 0);
+ QVERIFY(slot2Index > 0);
+ QVERIFY(slot3Index > 0);
+
+ QVERIFY( QMetaObject::connect( s, signal1Index, r1, slot1Index) );
+ QVERIFY( QMetaObject::connect( s, signal3Index, r2, slot3Index) );
+ QVERIFY( QMetaObject::connect( s, -1, r2, slot2Index) );
+
+ QCOMPARE( r1->count_slot1, 0 );
+ QCOMPARE( r1->count_slot2, 0 );
+ QCOMPARE( r1->count_slot3, 0 );
+ QCOMPARE( r2->count_slot1, 0 );
+ QCOMPARE( r2->count_slot2, 0 );
+ QCOMPARE( r2->count_slot3, 0 );
+
+ s->emitSignal1();
+
+ QCOMPARE( r1->count_slot1, 1 );
+ QCOMPARE( r1->count_slot2, 0 );
+ QCOMPARE( r1->count_slot3, 0 );
+ QCOMPARE( r2->count_slot1, 0 );
+ QCOMPARE( r2->count_slot2, 1 );
+ QCOMPARE( r2->count_slot3, 0 );
+
+ s->emitSignal2();
+ s->emitSignal3();
+ s->emitSignal4();
+
+ QCOMPARE( r1->count_slot1, 1 );
+ QCOMPARE( r1->count_slot2, 0 );
+ QCOMPARE( r1->count_slot3, 0 );
+ QCOMPARE( r2->count_slot1, 0 );
+ QCOMPARE( r2->count_slot2, 4 );
+ QCOMPARE( r2->count_slot3, 1 );
+
+ QVERIFY( QMetaObject::disconnect( s, signal1Index, r1, slot1Index) );
+ QVERIFY( QMetaObject::disconnect( s, signal3Index, r2, slot3Index) );
+ QVERIFY( QMetaObject::disconnect( s, -1, r2, slot2Index) );
+
+ s->emitSignal1();
+ s->emitSignal2();
+ s->emitSignal3();
+ s->emitSignal4();
+
+ QCOMPARE( r1->count_slot1, 1 );
+ QCOMPARE( r1->count_slot2, 0 );
+ QCOMPARE( r1->count_slot3, 0 );
+ QCOMPARE( r2->count_slot1, 0 );
+ QCOMPARE( r2->count_slot2, 4 );
+ QCOMPARE( r2->count_slot3, 1 );
+
+ //some "dynamic" signal
+ QVERIFY( QMetaObject::connect( s, s->metaObject()->methodOffset() + 20, r1, slot3Index) );
+ QVERIFY( QMetaObject::connect( s, s->metaObject()->methodOffset() + 35, r2, slot1Index) );
+ QVERIFY( QMetaObject::connect( s, -1, r1, slot2Index) );
+
+ r1->reset();
+ r2->reset();
+
+ void *args[] = { 0 , 0 };
+ QMetaObject::activate(s, s->metaObject()->methodOffset() + 20, args);
+ QMetaObject::activate(s, s->metaObject()->methodOffset() + 48, args);
+ QCOMPARE( r1->count_slot1, 0 );
+ QCOMPARE( r1->count_slot2, 2 );
+ QCOMPARE( r1->count_slot3, 1 );
+ QCOMPARE( r2->count_slot1, 0 );
+ QCOMPARE( r2->count_slot2, 0 );
+ QCOMPARE( r2->count_slot3, 0 );
+
+ QMetaObject::activate(s, s->metaObject()->methodOffset() + 35, args);
+ s->emitSignal1();
+ s->emitSignal2();
+
+ QCOMPARE( r1->count_slot1, 0 );
+ QCOMPARE( r1->count_slot2, 5 );
+ QCOMPARE( r1->count_slot3, 1 );
+ QCOMPARE( r2->count_slot1, 1 );
+ QCOMPARE( r2->count_slot2, 0 );
+ QCOMPARE( r2->count_slot3, 0 );
+
+ delete s;
+ r1->reset();
+ r2->reset();
+
+#define SIGNAL_INDEX(S) obj1.metaObject()->indexOfSignal(QMetaObject::normalizedSignature(#S))
+ OverloadObject obj1;
+ QObject obj2, obj3;
+
+ QMetaObject::connect(&obj1, SIGNAL_INDEX(sig(int)) , r1, slot1Index);
+ QMetaObject::connect(&obj1, SIGNAL_INDEX(sig(QObject *, QObject *, QObject *)) , r2, slot1Index);
+
+ QMetaObject::connect(&obj1, SIGNAL_INDEX(sig(QObject *, QObject *, QObject *, QObject *)) , r1, slot2Index);
+ QMetaObject::connect(&obj1, SIGNAL_INDEX(sig(QObject *)) , r2, slot2Index);
+ QMetaObject::connect(&obj1, SIGNAL_INDEX(sig(int, int)) , r1, slot3Index);
+
+ emit obj1.sig(0.5); //connected to nothing
+ emit obj1.sig(1, 'a'); //connected to nothing
+ QCOMPARE( r1->count_slot1, 0 );
+ QCOMPARE( r1->count_slot2, 0 );
+ QCOMPARE( r1->count_slot3, 0 );
+ QCOMPARE( r2->count_slot1, 0 );
+ QCOMPARE( r2->count_slot2, 0 );
+ QCOMPARE( r2->count_slot3, 0 );
+
+ emit obj1.sig(1); //this signal is connected
+ emit obj1.sig(&obj2);
+
+ QCOMPARE( r1->count_slot1, 1 );
+ QCOMPARE( r1->count_slot2, 0 );
+ QCOMPARE( r1->count_slot3, 1 );
+ QCOMPARE( r2->count_slot1, 0 );
+ QCOMPARE( r2->count_slot2, 1 );
+ QCOMPARE( r2->count_slot3, 0 );
+
+ emit obj1.sig(&obj2, &obj3); //this signal is connected
+
+ QCOMPARE( r1->count_slot1, 1 );
+ QCOMPARE( r1->count_slot2, 1 );
+ QCOMPARE( r1->count_slot3, 1 );
+ QCOMPARE( r2->count_slot1, 1 );
+ QCOMPARE( r2->count_slot2, 1 );
+ QCOMPARE( r2->count_slot3, 0 );
+
+ delete r1;
+ delete r2;
+
+}
+
+void tst_QObject::qMetaObjectDisconnectOne()
+{
+ SenderObject *s = new SenderObject;
+ ReceiverObject *r1 = new ReceiverObject;
+
+ int signal1Index = s->metaObject()->indexOfSignal("signal1()");
+ int signal3Index = s->metaObject()->indexOfSignal("signal3()");
+ int slot1Index = r1->metaObject()->indexOfSlot("slot1()");
+ int slot2Index = r1->metaObject()->indexOfSlot("slot2()");
+
+ QVERIFY(signal1Index > 0);
+ QVERIFY(signal3Index > 0);
+ QVERIFY(slot1Index > 0);
+ QVERIFY(slot2Index > 0);
+
+ QVERIFY( QMetaObject::connect(s, signal1Index, r1, slot1Index) );
+ QVERIFY( QMetaObject::connect(s, signal3Index, r1, slot2Index) );
+ QVERIFY( QMetaObject::connect(s, signal3Index, r1, slot2Index) );
+ QVERIFY( QMetaObject::connect(s, signal3Index, r1, slot2Index) );
+
+ r1->reset();
+ QCOMPARE( r1->count_slot1, 0 );
+ QCOMPARE( r1->count_slot2, 0 );
+
+ s->emitSignal1();
+ QCOMPARE( r1->count_slot1, 1 );
+ QCOMPARE( r1->count_slot2, 0 );
+
+ s->emitSignal3();
+ QCOMPARE( r1->count_slot1, 1 );
+ QCOMPARE( r1->count_slot2, 3 );
+
+ r1->reset();
+ QVERIFY( QMetaObject::disconnectOne(s, signal1Index, r1, slot1Index) );
+ QVERIFY( QMetaObject::disconnectOne(s, signal3Index, r1, slot2Index) );
+
+ s->emitSignal1();
+ QCOMPARE( r1->count_slot1, 0 );
+ QCOMPARE( r1->count_slot2, 0 );
+
+ s->emitSignal3();
+ QCOMPARE( r1->count_slot1, 0 );
+ QCOMPARE( r1->count_slot2, 2 );
+
+ r1->reset();
+ QVERIFY( false == QMetaObject::disconnectOne(s, signal1Index, r1, slot1Index) );
+ QVERIFY( QMetaObject::disconnectOne(s, signal3Index, r1, slot2Index) );
+
+ s->emitSignal1();
+ QCOMPARE( r1->count_slot1, 0 );
+ QCOMPARE( r1->count_slot2, 0 );
+
+ s->emitSignal3();
+ QCOMPARE( r1->count_slot1, 0 );
+ QCOMPARE( r1->count_slot2, 1 );
+
+ r1->reset();
+ QVERIFY( false == QMetaObject::disconnectOne(s, signal1Index, r1, slot1Index) );
+ QVERIFY( QMetaObject::disconnectOne(s, signal3Index, r1, slot2Index) );
+
+ s->emitSignal1();
+ QCOMPARE( r1->count_slot1, 0 );
+ QCOMPARE( r1->count_slot2, 0 );
+
+ s->emitSignal3();
+ QCOMPARE( r1->count_slot1, 0 );
+ QCOMPARE( r1->count_slot2, 0 );
+
+ delete s;
+ delete r1;
+}
+
+class ConfusingObject : public SenderObject
+{ Q_OBJECT
+public slots:
+ void signal1() { s++; }
+signals:
+ void aPublicSlot();
+public:
+ int s;
+ ConfusingObject() : s(0) {}
+ friend class tst_QObject;
+};
+
+void tst_QObject::sameName()
+{
+ ConfusingObject c1, c2;
+ QVERIFY(connect(&c1, SIGNAL(signal1()), &c1, SLOT(signal1())));
+ c1.emitSignal1();
+ QCOMPARE(c1.s, 1);
+
+ QVERIFY(connect(&c2, SIGNAL(signal1()), &c1, SIGNAL(signal1())));
+ c2.emitSignal1();
+ QCOMPARE(c1.s, 2);
+
+ QVERIFY(connect(&c2, SIGNAL(aPublicSlot()), &c1, SLOT(signal1())));
+ c2.aPublicSlot();
+ QCOMPARE(c2.aPublicSlotCalled, 0);
+ QCOMPARE(c1.aPublicSlotCalled, 0);
+ QCOMPARE(c1.s, 3);
+
+ QVERIFY(connect(&c2, SIGNAL(aPublicSlot()), &c1, SLOT(aPublicSlot())));
+ c2.aPublicSlot();
+ QCOMPARE(c2.aPublicSlotCalled, 0);
+ QCOMPARE(c1.aPublicSlotCalled, 1);
+ QCOMPARE(c1.s, 4);
+}
+
+void tst_QObject::connectByMetaMethods()
+{
+ SenderObject s;
+ ReceiverObject r;
+ const QMetaObject *smeta = s.metaObject();
+ const QMetaObject *rmeta = r.metaObject();
+ int sigIndx = smeta->indexOfSignal(QMetaObject::normalizedSignature("signal1()"));
+ int slotIndx = rmeta->indexOfSlot(QMetaObject::normalizedSignature("slot1()"));
+ QVERIFY( sigIndx != -1 );
+ QVERIFY( slotIndx != -1 );
+ QMetaMethod signal = smeta->method(sigIndx);
+ QMetaMethod slot = rmeta->method(slotIndx);
+
+ QVERIFY(connect(&s,signal, &r,slot));
+
+ QVERIFY(!r.called(1));
+ s.emitSignal1();
+ QVERIFY(r.called(1));
+}
+
+void tst_QObject::connectByMetaMethodSlotInsteadOfSignal()
+{
+ SenderObject s;
+ ReceiverObject r;
+ const QMetaObject *smeta = s.metaObject();
+ const QMetaObject *rmeta = r.metaObject();
+ int badIndx = smeta->indexOfSlot(QMetaObject::normalizedSignature("aPublicSlot()"));
+ int slotIndx = rmeta->indexOfSlot(QMetaObject::normalizedSignature("slot1()"));
+ QVERIFY( badIndx != -1 );
+ QVERIFY( slotIndx != -1 );
+ QMetaMethod badMethod = smeta->method(badIndx);
+ QMetaMethod slot = rmeta->method(slotIndx);
+
+ QTest::ignoreMessage(QtWarningMsg,"QObject::connect: Cannot connect SenderObject::aPublicSlot() to ReceiverObject::slot1()");
+ QVERIFY(!connect(&s,badMethod, &r,slot));
+}
+
+class Constructable: public QObject
+{
+ Q_OBJECT
+
+public:
+ Q_INVOKABLE Constructable(){}
+
+};
+
+void tst_QObject::connectConstructorByMetaMethod()
+{
+ Constructable sc;
+ Constructable rc;
+ SenderObject s;
+ ReceiverObject r;
+
+ const QMetaObject cmeta = Constructable::staticMetaObject;
+ const QMetaObject *smeta = s.metaObject();
+ const QMetaObject *rmeta = r.metaObject();
+ int constructorIndx = cmeta.indexOfConstructor(QMetaObject::normalizedSignature("Constructable()"));
+ int sigIndx = smeta->indexOfSignal(QMetaObject::normalizedSignature("signal1()"));
+ int slotIndx = rmeta->indexOfSlot(QMetaObject::normalizedSignature("slot1()"));
+ QVERIFY( constructorIndx != -1 );
+ QVERIFY( sigIndx != -1 );
+ QVERIFY( slotIndx != -1 );
+
+ QMetaMethod constructor = cmeta.constructor(constructorIndx);
+ QMetaMethod signal = smeta->method(sigIndx);
+ QMetaMethod slot = rmeta->method(slotIndx);
+
+ QTest::ignoreMessage(QtWarningMsg,"QObject::connect: Cannot connect Constructable::Constructable() to ReceiverObject::slot1()");
+ QVERIFY(!connect(&sc,constructor, &r,slot));
+ QTest::ignoreMessage(QtWarningMsg,"QObject::connect: Cannot connect SenderObject::signal1() to Constructable::Constructable()");
+ QVERIFY(!connect(&s,signal, &rc,constructor));
+ QTest::ignoreMessage(QtWarningMsg,"QObject::connect: Cannot connect Constructable::Constructable() to Constructable::Constructable()");
+ QVERIFY(!connect(&sc,constructor, &rc,constructor));
+}
+
+void tst_QObject::disconnectByMetaMethod()
+{
+ SenderObject *s = new SenderObject;
+ ReceiverObject *r1 = new ReceiverObject;
+ ReceiverObject *r2 = new ReceiverObject;
+
+ QMetaMethod signal1 = s->metaObject()->method(
+ s->metaObject()->indexOfMethod("signal1()"));
+ QMetaMethod signal2 = s->metaObject()->method(
+ s->metaObject()->indexOfMethod("signal2()"));
+ QMetaMethod signal3 = s->metaObject()->method(
+ s->metaObject()->indexOfMethod("signal3()"));
+ QMetaMethod signal4 = s->metaObject()->method(
+ s->metaObject()->indexOfMethod("signal4()"));
+
+ QMetaMethod slot1 = r1->metaObject()->method(
+ r1->metaObject()->indexOfMethod("slot1()"));
+ QMetaMethod slot2 = r1->metaObject()->method(
+ r1->metaObject()->indexOfMethod("slot2()"));
+ QMetaMethod slot3 = r1->metaObject()->method(
+ r1->metaObject()->indexOfMethod("slot3()"));
+ QMetaMethod slot4 = r1->metaObject()->method(
+ r1->metaObject()->indexOfMethod("slot4()"));
+
+ connect(s, signal1, r1, slot1);
+
+ s->emitSignal1();
+
+ QVERIFY(r1->called(1));
+ r1->reset();
+
+ // usual disconnect with all parameters given
+ bool ret = QObject::disconnect(s, signal1, r1, slot1);
+
+ s->emitSignal1();
+
+ QVERIFY(!r1->called(1));
+ r1->reset();
+
+ QVERIFY(ret);
+ ret = QObject::disconnect(s, signal1, r1, slot1);
+ QVERIFY(!ret);
+
+ r1->reset();
+
+ connect( s, signal1, r1, slot1 );
+ connect( s, signal1, r1, slot2 );
+ connect( s, signal1, r1, slot3 );
+ connect( s, signal2, r1, slot4 );
+
+ // disconnect s's signal1() from all slots of r1
+ QObject::disconnect(s, signal1, r1, QMetaMethod());
+
+ s->emitSignal1();
+ s->emitSignal2();
+
+ QVERIFY(!r1->called(1));
+ QVERIFY(!r1->called(2));
+ QVERIFY(!r1->called(3));
+ QVERIFY(r1->called(4));
+ r1->reset();
+ // make sure all is disconnected again
+ QObject::disconnect(s, 0, r1, 0);
+
+ connect(s, signal1, r1, slot1);
+ connect(s, signal1, r2, slot1);
+ connect(s, signal2, r1, slot2);
+ connect(s, signal2, r2, slot2);
+ connect(s, signal3, r1, slot3);
+ connect(s, signal3, r2, slot3);
+
+ // disconnect signal1() from all receivers
+ QObject::disconnect(s, signal1, 0, QMetaMethod());
+ s->emitSignal1();
+ s->emitSignal2();
+ s->emitSignal3();
+
+ QVERIFY(!r1->called(1));
+ QVERIFY(!r2->called(1));
+ QVERIFY(r1->called(2));
+ QVERIFY(r2->called(2));
+ QVERIFY(r1->called(2));
+ QVERIFY(r2->called(2));
+
+ r1->reset();
+ r2->reset();
+
+ // disconnect all signals of s from all receivers
+ QObject::disconnect( s, 0, 0, 0 );
+
+ connect( s, signal1, r1, slot1 );
+ connect( s, signal1, r2, slot1 );
+
+ // disconnect all signals from slot1 of r1
+ QObject::disconnect(s, QMetaMethod(), r1, slot1);
+
+ s->emitSignal1();
+
+ QVERIFY(!r1->called(1));
+ QVERIFY(r2->called(1));
+
+ delete r2;
+ delete r1;
+ delete s;
+}
+
+void tst_QObject::disconnectNotSignalMetaMethod()
+{
+ SenderObject s;
+ ReceiverObject r;
+
+ connect(&s, SIGNAL(signal1()), &r, SLOT(slot1()));
+
+ QMetaMethod slot = s.metaObject()->method(
+ s.metaObject()->indexOfMethod("aPublicSlot()"));
+
+ QTest::ignoreMessage(QtWarningMsg,"Object::disconnect: Attempt to unbind non-signal SenderObject::aPublicSlot()");
+ QVERIFY(!QObject::disconnect(&s, slot, &r, QMetaMethod()));
+}
+
+class ThreadAffinityThread : public QThread
+{
+public:
+ SenderObject *sender;
+
+ ThreadAffinityThread(SenderObject *sender)
+ : sender(sender)
+ { }
+ void run()
+ {
+ sender->emitSignal1();
+ }
+};
+
+void tst_QObject::autoConnectionBehavior()
+{
+ SenderObject *sender = new SenderObject;
+ ReceiverObject *receiver = new ReceiverObject;
+ connect(sender, SIGNAL(signal1()), receiver, SLOT(slot1()));
+
+ // at emit, currentThread == sender->thread(), currentThread == receiver->thread(), sender->thread() == receiver->thread()
+ QVERIFY(!receiver->called(1));
+ sender->emitSignal1();
+ QVERIFY(receiver->called(1));
+ receiver->reset();
+
+ // at emit, currentThread != sender->thread(), currentThread != receiver->thread(), sender->thread() == receiver->thread()
+ ThreadAffinityThread emitThread1(sender);
+ QVERIFY(!receiver->called(1));
+ emitThread1.start();
+ QVERIFY(emitThread1.wait(30000));
+ QVERIFY(!receiver->called(1));
+ QCoreApplication::sendPostedEvents(receiver, QEvent::MetaCall);
+ QVERIFY(receiver->called(1));
+ receiver->reset();
+
+ // at emit, currentThread == sender->thread(), currentThread != receiver->thread(), sender->thread() != receiver->thread()
+ sender->moveToThread(&emitThread1);
+ QVERIFY(!receiver->called(1));
+ emitThread1.start();
+ QVERIFY(emitThread1.wait(30000));
+ QVERIFY(!receiver->called(1));
+ QCoreApplication::sendPostedEvents(receiver, QEvent::MetaCall);
+ QVERIFY(receiver->called(1));
+ receiver->reset();
+
+ // at emit, currentThread != sender->thread(), currentThread == receiver->thread(), sender->thread() != receiver->thread()
+ QVERIFY(!receiver->called(1));
+ sender->emitSignal1();
+ QVERIFY(receiver->called(1));
+ receiver->reset();
+
+ // at emit, currentThread != sender->thread(), currentThread != receiver->thread(), sender->thread() != receiver->thread()
+ ThreadAffinityThread emitThread2(sender);
+ QThread receiverThread;
+ QTimer *timer = new QTimer;
+ timer->setSingleShot(true);
+ timer->setInterval(100);
+ connect(&receiverThread, SIGNAL(started()), timer, SLOT(start()));
+ connect(timer, SIGNAL(timeout()), &receiverThread, SLOT(quit()), Qt::DirectConnection);
+ connect(&receiverThread, SIGNAL(finished()), timer, SLOT(deleteLater()));
+ timer->moveToThread(&receiverThread);
+
+ receiver->moveToThread(&receiverThread);
+ QVERIFY(!receiver->called(1));
+ emitThread2.start();
+ QVERIFY(emitThread2.wait(30000));
+ QVERIFY(!receiver->called(1));
+ receiverThread.start();
+ QVERIFY(receiverThread.wait(30000));
+ QVERIFY(receiver->called(1));
+ receiver->reset();
+
+ delete sender;
+ delete receiver;
+}
+
+class BaseDestroyed : public QObject
+{ Q_OBJECT
+ QList<QString> fooList;
+ bool destroyed;
+public:
+ BaseDestroyed() : destroyed(false)
+ { fooList << "a" << "b"; }
+ ~BaseDestroyed()
+ {
+ QVERIFY(!destroyed);
+ destroyed = true;
+ }
+
+public slots:
+ void slotUseList()
+ {
+ QVERIFY(!destroyed);
+ fooList << "c" << "d";
+ }
+};
+
+void tst_QObject::baseDestroyed()
+{
+ BaseDestroyed d;
+ connect(&d, SIGNAL(destroyed()), &d, SLOT(slotUseList()));
+ //When d goes out of scope, slotUseList should not be called as the BaseDestroyed has
+ // already been destroyed while ~QObject emit destroyed
+}
+
+QTEST_MAIN(tst_QObject)
+#include "tst_qobject.moc"
diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.pro b/tests/auto/corelib/kernel/qobject/tst_qobject.pro
new file mode 100644
index 0000000000..29de071328
--- /dev/null
+++ b/tests/auto/corelib/kernel/qobject/tst_qobject.pro
@@ -0,0 +1,21 @@
+load(qttest_p4)
+SOURCES += tst_qobject.cpp
+
+# this is here for a reason, moc_oldnormalizedobject.cpp is not auto-generated, it was generated by
+# moc from Qt 4.6, and should *not* be generated by the current moc
+SOURCES += moc_oldnormalizeobject.cpp
+
+QT = core \
+ network \
+ gui \
+ core-private
+wince*: {
+ addFiles.files = signalbug.exe
+ addFiles.path = .
+ DEPLOYMENT += addFiles
+}
+symbian: {
+ addFiles.files = signalbug.exe
+ addFiles.path = \\sys\\bin
+ DEPLOYMENT += addFiles
+}
diff --git a/tests/auto/corelib/kernel/qpointer/.gitignore b/tests/auto/corelib/kernel/qpointer/.gitignore
new file mode 100644
index 0000000000..ce97ab782d
--- /dev/null
+++ b/tests/auto/corelib/kernel/qpointer/.gitignore
@@ -0,0 +1 @@
+tst_qpointer
diff --git a/tests/auto/corelib/kernel/qpointer/qpointer.pro b/tests/auto/corelib/kernel/qpointer/qpointer.pro
new file mode 100644
index 0000000000..5728b7bb4f
--- /dev/null
+++ b/tests/auto/corelib/kernel/qpointer/qpointer.pro
@@ -0,0 +1,5 @@
+load(qttest_p4)
+QT += widgets
+SOURCES += tst_qpointer.cpp
+
+
diff --git a/tests/auto/corelib/kernel/qpointer/tst_qpointer.cpp b/tests/auto/corelib/kernel/qpointer/tst_qpointer.cpp
new file mode 100644
index 0000000000..fed98ec86a
--- /dev/null
+++ b/tests/auto/corelib/kernel/qpointer/tst_qpointer.cpp
@@ -0,0 +1,343 @@
+/****************************************************************************
+**
+** 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 <QPointer>
+#include <QWidget>
+
+class tst_QPointer : public QObject
+{
+ Q_OBJECT
+public:
+ inline tst_QPointer *me() const
+ { return const_cast<tst_QPointer *>(this); }
+
+private slots:
+ void constructors();
+ void destructor();
+ void assignment_operators();
+ void equality_operators();
+ void isNull();
+ void dereference_operators();
+ void disconnect();
+ void castDuringDestruction();
+ void threadSafety();
+};
+
+void tst_QPointer::constructors()
+{
+ QPointer<QObject> p1;
+ QPointer<QObject> p2(this);
+ QPointer<QObject> p3(p2);
+ QCOMPARE(p1, QPointer<QObject>(0));
+ QCOMPARE(p2, QPointer<QObject>(this));
+ QCOMPARE(p3, QPointer<QObject>(this));
+}
+
+void tst_QPointer::destructor()
+{
+ // Make two QPointer's to the same object
+ QObject *object = new QObject;
+ QPointer<QObject> p1 = object;
+ QPointer<QObject> p2 = object;
+ // Check that they point to the correct object
+ QCOMPARE(p1, QPointer<QObject>(object));
+ QCOMPARE(p2, QPointer<QObject>(object));
+ QCOMPARE(p1, p2);
+ // Destroy the guarded object
+ delete object;
+ // Check that both pointers were zeroed
+ QCOMPARE(p1, QPointer<QObject>(0));
+ QCOMPARE(p2, QPointer<QObject>(0));
+ QCOMPARE(p1, p2);
+}
+
+void tst_QPointer::assignment_operators()
+{
+ QPointer<QObject> p1;
+ QPointer<QObject> p2;
+
+ // Test assignment with a QObject-derived object pointer
+ p1 = this;
+ p2 = p1;
+ QCOMPARE(p1, QPointer<QObject>(this));
+ QCOMPARE(p2, QPointer<QObject>(this));
+ QCOMPARE(p1, QPointer<QObject>(p2));
+
+ // Test assignment with a null pointer
+ p1 = 0;
+ p2 = p1;
+ QCOMPARE(p1, QPointer<QObject>(0));
+ QCOMPARE(p2, QPointer<QObject>(0));
+ QCOMPARE(p1, QPointer<QObject>(p2));
+
+ // Test assignment with a real QObject pointer
+ QObject *object = new QObject;
+
+ p1 = object;
+ p2 = p1;
+ QCOMPARE(p1, QPointer<QObject>(object));
+ QCOMPARE(p2, QPointer<QObject>(object));
+ QCOMPARE(p1, QPointer<QObject>(p2));
+
+ // Test assignment with the same pointer that's already guarded
+ p1 = object;
+ p2 = p1;
+ QCOMPARE(p1, QPointer<QObject>(object));
+ QCOMPARE(p2, QPointer<QObject>(object));
+ QCOMPARE(p1, QPointer<QObject>(p2));
+
+ // Cleanup
+ delete object;
+}
+
+void tst_QPointer::equality_operators()
+{
+ QPointer<QObject> p1;
+ QPointer<QObject> p2;
+
+ QVERIFY(p1 == p2);
+
+ QObject *object = 0;
+ QWidget *widget = 0;
+
+ p1 = object;
+ QVERIFY(p1 == p2);
+ QVERIFY(p1 == object);
+ p2 = object;
+ QVERIFY(p2 == p1);
+ QVERIFY(p2 == object);
+
+ p1 = this;
+ QVERIFY(p1 != p2);
+ p2 = p1;
+ QVERIFY(p1 == p2);
+
+ // compare to zero
+ p1 = 0;
+ QVERIFY(p1 == 0);
+ QVERIFY(0 == p1);
+ QVERIFY(p2 != 0);
+ QVERIFY(0 != p2);
+ QVERIFY(p1 == object);
+ QVERIFY(object == p1);
+ QVERIFY(p2 != object);
+ QVERIFY(object != p2);
+ QVERIFY(p1 == widget);
+ QVERIFY(widget == p1);
+ QVERIFY(p2 != widget);
+ QVERIFY(widget != p2);
+}
+
+void tst_QPointer::isNull()
+{
+ QPointer<QObject> p1;
+ QVERIFY(p1.isNull());
+ p1 = this;
+ QVERIFY(!p1.isNull());
+ p1 = 0;
+ QVERIFY(p1.isNull());
+}
+
+void tst_QPointer::dereference_operators()
+{
+ QPointer<tst_QPointer> p1 = this;
+ QPointer<tst_QPointer> p2;
+
+ // operator->() -- only makes sense if not null
+ QObject *object = p1->me();
+ QCOMPARE(object, this);
+
+ // operator*() -- only makes sense if not null
+ QObject &ref = *p1;
+ QCOMPARE(&ref, this);
+
+ // operator T*()
+ QCOMPARE(static_cast<QObject *>(p1), this);
+ QCOMPARE(static_cast<QObject *>(p2), static_cast<QObject *>(0));
+
+ // data()
+ QCOMPARE(p1.data(), this);
+ QCOMPARE(p2.data(), static_cast<QObject *>(0));
+}
+
+void tst_QPointer::disconnect()
+{
+ // Verify that pointer remains guarded when all signals are disconencted.
+ QPointer<QObject> p1 = new QObject;
+ QVERIFY(!p1.isNull());
+ p1->disconnect();
+ QVERIFY(!p1.isNull());
+ delete static_cast<QObject *>(p1);
+ QVERIFY(p1.isNull());
+}
+
+class ChildObject : public QObject
+{
+ QPointer<QObject> guardedPointer;
+
+public:
+ ChildObject(QObject *parent)
+ : QObject(parent), guardedPointer(parent)
+ { }
+ ~ChildObject();
+};
+
+ChildObject::~ChildObject()
+{
+ QCOMPARE(static_cast<QObject *>(guardedPointer), static_cast<QObject *>(0));
+ QCOMPARE(qobject_cast<QObject *>(guardedPointer), static_cast<QObject *>(0));
+}
+
+class ChildWidget : public QWidget
+{
+ QPointer<QWidget> guardedPointer;
+
+public:
+ ChildWidget(QWidget *parent)
+ : QWidget(parent), guardedPointer(parent)
+ { }
+ ~ChildWidget();
+};
+
+ChildWidget::~ChildWidget()
+{
+ QCOMPARE(static_cast<QWidget *>(guardedPointer), static_cast<QWidget *>(0));
+ QCOMPARE(qobject_cast<QWidget *>(guardedPointer), static_cast<QWidget *>(0));
+}
+
+class DerivedChild;
+
+class DerivedParent : public QObject
+{
+ Q_OBJECT
+
+ DerivedChild *derivedChild;
+
+public:
+ DerivedParent();
+ ~DerivedParent();
+};
+
+class DerivedChild : public QObject
+{
+ Q_OBJECT
+
+ DerivedParent *parentPointer;
+ QPointer<DerivedParent> guardedParentPointer;
+
+public:
+ DerivedChild(DerivedParent *parent)
+ : QObject(parent), parentPointer(parent), guardedParentPointer(parent)
+ { }
+ ~DerivedChild();
+};
+
+DerivedParent::DerivedParent()
+ : QObject()
+{
+ derivedChild = new DerivedChild(this);
+}
+
+DerivedParent::~DerivedParent()
+{
+ delete derivedChild;
+}
+
+DerivedChild::~DerivedChild()
+{
+ QCOMPARE(static_cast<DerivedParent *>(guardedParentPointer), parentPointer);
+ QCOMPARE(qobject_cast<DerivedParent *>(guardedParentPointer), parentPointer);
+}
+
+void tst_QPointer::castDuringDestruction()
+{
+ {
+ QObject *parentObject = new QObject();
+ (void) new ChildObject(parentObject);
+ delete parentObject;
+ }
+
+ {
+ QWidget *parentWidget = new QWidget();
+ (void) new ChildWidget(parentWidget);
+ delete parentWidget;
+ }
+
+ {
+ delete new DerivedParent();
+ }
+}
+
+class TestRunnable : public QObject, public QRunnable {
+ void run() {
+ QPointer<QObject> obj1 = new QObject;
+ QPointer<QObject> obj2 = new QObject;
+ obj1->moveToThread(thread()); // this is the owner thread
+ obj1->deleteLater(); // the delete will happen in the owner thread
+ obj2->moveToThread(thread()); // this is the owner thread
+ obj2->deleteLater(); // the delete will happen in the owner thread
+ }
+};
+
+void tst_QPointer::threadSafety()
+{
+
+ QThread owner;
+ owner.start();
+
+ QThreadPool pool;
+ for (int i = 0; i < 300; i++) {
+ QPointer<TestRunnable> task = new TestRunnable;
+ task->setAutoDelete(true);
+ task->moveToThread(&owner);
+ pool.start(task);
+ }
+ pool.waitForDone();
+
+ owner.quit();
+ owner.wait();
+}
+
+
+QTEST_MAIN(tst_QPointer)
+#include "tst_qpointer.moc"
diff --git a/tests/auto/corelib/kernel/qsignalmapper/.gitignore b/tests/auto/corelib/kernel/qsignalmapper/.gitignore
new file mode 100644
index 0000000000..33fab268da
--- /dev/null
+++ b/tests/auto/corelib/kernel/qsignalmapper/.gitignore
@@ -0,0 +1 @@
+tst_qsignalmapper
diff --git a/tests/auto/corelib/kernel/qsignalmapper/qsignalmapper.pro b/tests/auto/corelib/kernel/qsignalmapper/qsignalmapper.pro
new file mode 100644
index 0000000000..5af4b70c63
--- /dev/null
+++ b/tests/auto/corelib/kernel/qsignalmapper/qsignalmapper.pro
@@ -0,0 +1,6 @@
+load(qttest_p4)
+QT += widgets
+SOURCES += tst_qsignalmapper.cpp
+
+
+
diff --git a/tests/auto/corelib/kernel/qsignalmapper/tst_qsignalmapper.cpp b/tests/auto/corelib/kernel/qsignalmapper/tst_qsignalmapper.cpp
new file mode 100644
index 0000000000..a4dc123778
--- /dev/null
+++ b/tests/auto/corelib/kernel/qsignalmapper/tst_qsignalmapper.cpp
@@ -0,0 +1,156 @@
+/****************************************************************************
+**
+** 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 <qsignalmapper.h>
+#include <qspinbox.h>
+
+
+
+
+
+//TESTED_CLASS=
+//TESTED_FILES=
+
+class tst_QSignalMapper : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QSignalMapper();
+ virtual ~tst_QSignalMapper();
+
+
+public slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+private slots:
+ void mapped();
+};
+
+tst_QSignalMapper::tst_QSignalMapper()
+{
+}
+
+tst_QSignalMapper::~tst_QSignalMapper()
+{
+}
+
+void tst_QSignalMapper::initTestCase()
+{
+}
+
+void tst_QSignalMapper::cleanupTestCase()
+{
+}
+
+void tst_QSignalMapper::init()
+{
+}
+
+void tst_QSignalMapper::cleanup()
+{
+}
+
+class QtTestObject : public QObject
+{
+ Q_OBJECT
+public slots:
+ void myslot(int id);
+ void myslot(const QString &str);
+
+public:
+ int id;
+ QString str;
+};
+
+void QtTestObject::myslot(int id)
+{
+ this->id = id;
+}
+
+void QtTestObject::myslot(const QString &str)
+{
+ this->str = str;
+}
+
+void tst_QSignalMapper::mapped()
+{
+ QSignalMapper mapper(0);
+
+ QtTestObject target;
+ QSpinBox spinBox1(0);
+ QSpinBox spinBox2(0);
+ QSpinBox spinBox3(0);
+
+ connect(&spinBox1, SIGNAL(valueChanged(int)), &mapper, SLOT(map()));
+ connect(&spinBox2, SIGNAL(valueChanged(int)), &mapper, SLOT(map()));
+ connect(&spinBox3, SIGNAL(valueChanged(int)), &mapper, SLOT(map()));
+
+ mapper.setMapping(&spinBox1, 7);
+ mapper.setMapping(&spinBox1, 1);
+ mapper.setMapping(&spinBox2, 2);
+ mapper.setMapping(&spinBox2, "two");
+ mapper.setMapping(&spinBox3, "three");
+
+ connect(&mapper, SIGNAL(mapped(int)), &target, SLOT(myslot(int)));
+ connect(&mapper, SIGNAL(mapped(const QString &)), &target, SLOT(myslot(const QString &)));
+
+ spinBox1.setValue(20);
+ QVERIFY(target.id == 1);
+ QVERIFY(target.str.isEmpty());
+
+ spinBox2.setValue(20);
+ QVERIFY(target.id == 2);
+ QVERIFY(target.str == "two");
+
+ spinBox3.setValue(20);
+ QVERIFY(target.id == 2);
+ QVERIFY(target.str == "three");
+}
+
+QTEST_MAIN(tst_QSignalMapper)
+#include "tst_qsignalmapper.moc"
diff --git a/tests/auto/corelib/kernel/qsocketnotifier/.gitignore b/tests/auto/corelib/kernel/qsocketnotifier/.gitignore
new file mode 100644
index 0000000000..2e1568660c
--- /dev/null
+++ b/tests/auto/corelib/kernel/qsocketnotifier/.gitignore
@@ -0,0 +1 @@
+tst_qsocketnotifier
diff --git a/tests/auto/corelib/kernel/qsocketnotifier/qsocketnotifier.pro b/tests/auto/corelib/kernel/qsocketnotifier/qsocketnotifier.pro
new file mode 100644
index 0000000000..0768c69caa
--- /dev/null
+++ b/tests/auto/corelib/kernel/qsocketnotifier/qsocketnotifier.pro
@@ -0,0 +1,11 @@
+load(qttest_p4)
+SOURCES += tst_qsocketnotifier.cpp
+QT = core-private network-private
+
+requires(contains(QT_CONFIG,private_tests))
+
+include(../platformsocketengine/platformsocketengine.pri)
+
+symbian: TARGET.CAPABILITY = NetworkServices
+
+CONFIG += insignificant_test # QTBUG-21204
diff --git a/tests/auto/corelib/kernel/qsocketnotifier/tst_qsocketnotifier.cpp b/tests/auto/corelib/kernel/qsocketnotifier/tst_qsocketnotifier.cpp
new file mode 100644
index 0000000000..2c3a6c52bf
--- /dev/null
+++ b/tests/auto/corelib/kernel/qsocketnotifier/tst_qsocketnotifier.cpp
@@ -0,0 +1,367 @@
+/****************************************************************************
+**
+** 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/QTest>
+#include <QtTest/QSignalSpy>
+#include <QtTest/QTestEventLoop>
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QTimer>
+#include <QtCore/QSocketNotifier>
+#include <QtNetwork/QTcpServer>
+#include <QtNetwork/QTcpSocket>
+#ifdef Q_OS_SYMBIAN
+#include <private/qsymbiansocketengine_p.h>
+#define NATIVESOCKETENGINE QSymbianSocketEngine
+#else
+#include <private/qnativesocketengine_p.h>
+#define NATIVESOCKETENGINE QNativeSocketEngine
+#endif
+#ifdef Q_OS_UNIX
+#include <private/qnet_unix_p.h>
+#include <sys/select.h>
+#endif
+#include <limits>
+
+#if defined (Q_CC_MSVC) && defined(max)
+# undef max
+# undef min
+#endif // Q_CC_MSVC
+
+class tst_QSocketNotifier : public QObject
+{
+ Q_OBJECT
+public:
+ tst_QSocketNotifier();
+ ~tst_QSocketNotifier();
+
+private slots:
+ void unexpectedDisconnection();
+ void mixingWithTimers();
+ void posixSockets();
+ void bogusFds();
+};
+
+tst_QSocketNotifier::tst_QSocketNotifier()
+{ }
+
+tst_QSocketNotifier::~tst_QSocketNotifier()
+{
+}
+
+class UnexpectedDisconnectTester : public QObject
+{
+ Q_OBJECT
+public:
+ NATIVESOCKETENGINE *readEnd1, *readEnd2;
+ int sequence;
+
+ UnexpectedDisconnectTester(NATIVESOCKETENGINE *s1, NATIVESOCKETENGINE *s2)
+ : readEnd1(s1), readEnd2(s2), sequence(0)
+ {
+ QSocketNotifier *notifier1 =
+ new QSocketNotifier(readEnd1->socketDescriptor(), QSocketNotifier::Read, this);
+ connect(notifier1, SIGNAL(activated(int)), SLOT(handleActivated()));
+ QSocketNotifier *notifier2 =
+ new QSocketNotifier(readEnd2->socketDescriptor(), QSocketNotifier::Read, this);
+ connect(notifier2, SIGNAL(activated(int)), SLOT(handleActivated()));
+ }
+
+public slots:
+ void handleActivated()
+ {
+ char data1[1], data2[1];
+ ++sequence;
+ if (sequence == 1) {
+ // read from both ends
+ (void) readEnd1->read(data1, sizeof(data1));
+ (void) readEnd2->read(data2, sizeof(data2));
+ emit finished();
+ } else if (sequence == 2) {
+ // we should never get here
+ QCOMPARE(readEnd2->read(data2, sizeof(data2)), qint64(-2));
+ QVERIFY(readEnd2->isValid());
+ }
+ }
+
+signals:
+ void finished();
+};
+
+void tst_QSocketNotifier::unexpectedDisconnection()
+{
+#ifdef Q_OS_SYMBIAN
+ QSKIP("Symbian socket engine pseudo descriptors can't be used for QSocketNotifier", SkipAll);
+#else
+ /*
+ Given two sockets and two QSocketNotifiers registered on each
+ their socket. If both sockets receive data, and the first slot
+ invoked by one of the socket notifiers empties both sockets, the
+ other notifier will also emit activated(). This results in
+ unexpected disconnection in QAbstractSocket.
+
+ The use case is that somebody calls one of the
+ waitFor... functions in a QSocketNotifier activated slot, and
+ the waitFor... functions do local selects that can empty both
+ stdin and stderr while waiting for fex bytes to be written.
+ */
+
+ QTcpServer server;
+ QVERIFY(server.listen(QHostAddress::LocalHost, 0));
+
+ NATIVESOCKETENGINE readEnd1;
+ readEnd1.initialize(QAbstractSocket::TcpSocket);
+ bool b = readEnd1.connectToHost(server.serverAddress(), server.serverPort());
+ QVERIFY(readEnd1.waitForWrite());
+// while (!b && readEnd1.state() != QAbstractSocket::ConnectedState)
+// b = readEnd1.connectToHost(server.serverAddress(), server.serverPort());
+ QVERIFY(readEnd1.state() == QAbstractSocket::ConnectedState);
+ QVERIFY(server.waitForNewConnection());
+ QTcpSocket *writeEnd1 = server.nextPendingConnection();
+ QVERIFY(writeEnd1 != 0);
+
+ NATIVESOCKETENGINE readEnd2;
+ readEnd2.initialize(QAbstractSocket::TcpSocket);
+ b = readEnd2.connectToHost(server.serverAddress(), server.serverPort());
+ QVERIFY(readEnd2.waitForWrite());
+// while (!b)
+// b = readEnd2.connectToHost(server.serverAddress(), server.serverPort());
+ QVERIFY(readEnd2.state() == QAbstractSocket::ConnectedState);
+ QVERIFY(server.waitForNewConnection());
+ QTcpSocket *writeEnd2 = server.nextPendingConnection();
+ QVERIFY(writeEnd2 != 0);
+
+ writeEnd1->write("1", 1);
+ writeEnd2->write("2", 1);
+
+ writeEnd1->waitForBytesWritten();
+ writeEnd2->waitForBytesWritten();
+
+ writeEnd1->flush();
+ writeEnd2->flush();
+
+ UnexpectedDisconnectTester tester(&readEnd1, &readEnd2);
+
+ QTimer timer;
+ timer.setSingleShot(true);
+ timer.start(30000);
+ do {
+ // we have to wait until sequence value changes
+ // as any event can make us jump out processing
+ QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents);
+ QVERIFY(timer.isActive()); //escape if test would hang
+ } while(tester.sequence <= 0);
+
+ QVERIFY(readEnd1.state() == QAbstractSocket::ConnectedState);
+ QVERIFY(readEnd2.state() == QAbstractSocket::ConnectedState);
+
+ QCOMPARE(tester.sequence, 2);
+
+ readEnd1.close();
+ readEnd2.close();
+ writeEnd1->close();
+ writeEnd2->close();
+ server.close();
+#endif
+}
+
+class MixingWithTimersHelper : public QObject
+{
+ Q_OBJECT
+
+public:
+ MixingWithTimersHelper(QTimer *timer, QTcpServer *server);
+
+ bool timerActivated;
+ bool socketActivated;
+
+private slots:
+ void timerFired();
+ void socketFired();
+};
+
+MixingWithTimersHelper::MixingWithTimersHelper(QTimer *timer, QTcpServer *server)
+{
+ timerActivated = false;
+ socketActivated = false;
+
+ connect(timer, SIGNAL(timeout()), SLOT(timerFired()));
+ connect(server, SIGNAL(newConnection()), SLOT(socketFired()));
+}
+
+void MixingWithTimersHelper::timerFired()
+{
+ timerActivated = true;
+}
+
+void MixingWithTimersHelper::socketFired()
+{
+ socketActivated = true;
+}
+
+void tst_QSocketNotifier::mixingWithTimers()
+{
+ QTimer timer;
+ timer.setInterval(0);
+ timer.start();
+
+ QTcpServer server;
+ QVERIFY(server.listen(QHostAddress::LocalHost, 0));
+
+ MixingWithTimersHelper helper(&timer, &server);
+
+ QCoreApplication::processEvents();
+
+ QCOMPARE(helper.timerActivated, true);
+ QCOMPARE(helper.socketActivated, false);
+
+ helper.timerActivated = false;
+ helper.socketActivated = false;
+
+ QTcpSocket socket;
+ socket.connectToHost(server.serverAddress(), server.serverPort());
+
+ QCoreApplication::processEvents();
+
+ QCOMPARE(helper.timerActivated, true);
+ QCOMPARE(helper.socketActivated, true);
+}
+
+void tst_QSocketNotifier::posixSockets()
+{
+#ifndef Q_OS_UNIX
+ QSKIP("test only for posix", SkipAll);
+#else
+
+ QTcpServer server;
+ QVERIFY(server.listen(QHostAddress::LocalHost, 0));
+
+ int posixSocket = qt_safe_socket(AF_INET, SOCK_STREAM, 0);
+ sockaddr_in addr;
+ addr.sin_addr.s_addr = htonl(0x7f000001);
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(server.serverPort());
+ qt_safe_connect(posixSocket, (const struct sockaddr*)&addr, sizeof(sockaddr_in));
+ QVERIFY(server.waitForNewConnection(5000));
+ QScopedPointer<QTcpSocket> passive(server.nextPendingConnection());
+
+ ::fcntl(posixSocket, F_SETFL, ::fcntl(posixSocket, F_GETFL) | O_NONBLOCK);
+
+ {
+ QSocketNotifier rn(posixSocket, QSocketNotifier::Read);
+ connect(&rn, SIGNAL(activated(int)), &QTestEventLoop::instance(), SLOT(exitLoop()));
+ QSignalSpy readSpy(&rn, SIGNAL(activated(int)));
+ QSocketNotifier wn(posixSocket, QSocketNotifier::Write);
+ connect(&wn, SIGNAL(activated(int)), &QTestEventLoop::instance(), SLOT(exitLoop()));
+ QSignalSpy writeSpy(&wn, SIGNAL(activated(int)));
+ QSocketNotifier en(posixSocket, QSocketNotifier::Exception);
+ connect(&en, SIGNAL(activated(int)), &QTestEventLoop::instance(), SLOT(exitLoop()));
+ QSignalSpy errorSpy(&en, SIGNAL(activated(int)));
+
+ passive->write("hello",6);
+ passive->waitForBytesWritten(5000);
+
+ QTestEventLoop::instance().enterLoop(3);
+ QCOMPARE(readSpy.count(), 1);
+ QEXPECT_FAIL("", "QTBUG-20982 fails", Abort);
+ QCOMPARE(writeSpy.count(), 0);
+ QCOMPARE(errorSpy.count(), 0);
+
+ char buffer[100];
+ qt_safe_read(posixSocket, buffer, 100);
+ QCOMPARE(buffer, "hello");
+
+ qt_safe_write(posixSocket, "goodbye", 8);
+
+ QTestEventLoop::instance().enterLoop(3);
+ QCOMPARE(readSpy.count(), 1);
+ QCOMPARE(writeSpy.count(), 1);
+ QCOMPARE(errorSpy.count(), 0);
+ QCOMPARE(passive->readAll(), QByteArray("goodbye",8));
+ }
+ qt_safe_close(posixSocket);
+#endif
+}
+
+void tst_QSocketNotifier::bogusFds()
+{
+#ifndef Q_OS_WIN
+ QTest::ignoreMessage(QtWarningMsg, "QSocketNotifier: Internal error");
+#endif
+ QSocketNotifier max(std::numeric_limits<int>::max(), QSocketNotifier::Read);
+ QTest::ignoreMessage(QtWarningMsg, "QSocketNotifier: Invalid socket specified");
+#ifndef Q_OS_WIN
+ // FIXME QTBUG-20982: this fails, and ignoreMessage can't be skipped or QEXPECT_FAILed
+ // QTest::ignoreMessage(QtWarningMsg, "QSocketNotifier: Internal error");
+#endif
+ QSocketNotifier min(std::numeric_limits<int>::min(), QSocketNotifier::Write);
+#ifndef Q_OS_WIN
+ // FIXME QTBUG-20982: this fails, and ignoreMessage can't be skipped or QEXPECT_FAILed
+ // QTest::ignoreMessage(QtWarningMsg, "QSocketNotifier: Internal error");
+#endif
+ //bogus magic number is the first pseudo socket descriptor from symbian socket engine.
+ QSocketNotifier bogus(0x40000000, QSocketNotifier::Exception);
+ QSocketNotifier largestlegal(FD_SETSIZE - 1, QSocketNotifier::Read);
+
+ QSignalSpy maxspy(&max, SIGNAL(activated(int)));
+ QSignalSpy minspy(&min, SIGNAL(activated(int)));
+ QSignalSpy bogspy(&bogus, SIGNAL(activated(int)));
+ QSignalSpy llspy(&largestlegal, SIGNAL(activated(int)));
+
+ //generate some unrelated socket activity
+ QTcpServer server;
+ QVERIFY(server.listen(QHostAddress::LocalHost));
+ connect(&server, SIGNAL(newConnection()), &QTestEventLoop::instance(), SLOT(exitLoop()));
+ QTcpSocket client;
+ client.connectToHost(QHostAddress::LocalHost, server.serverPort());
+ QTestEventLoop::instance().enterLoop(5);
+ QVERIFY(server.hasPendingConnections());
+
+ //check no activity on bogus notifiers
+ QCOMPARE(maxspy.count(), 0);
+ QCOMPARE(minspy.count(), 0);
+ QCOMPARE(bogspy.count(), 0);
+ QCOMPARE(llspy.count(), 0);
+}
+
+QTEST_MAIN(tst_QSocketNotifier)
+#include <tst_qsocketnotifier.moc>
diff --git a/tests/auto/corelib/kernel/qtimer/.gitignore b/tests/auto/corelib/kernel/qtimer/.gitignore
new file mode 100644
index 0000000000..14fd00629e
--- /dev/null
+++ b/tests/auto/corelib/kernel/qtimer/.gitignore
@@ -0,0 +1 @@
+tst_qtimer
diff --git a/tests/auto/corelib/kernel/qtimer/qtimer.pro b/tests/auto/corelib/kernel/qtimer/qtimer.pro
new file mode 100644
index 0000000000..086df1dd18
--- /dev/null
+++ b/tests/auto/corelib/kernel/qtimer/qtimer.pro
@@ -0,0 +1,5 @@
+load(qttest_p4)
+QT = core
+SOURCES += tst_qtimer.cpp
+QT = core
+CONFIG += parallel_test
diff --git a/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp b/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp
new file mode 100644
index 0000000000..a8ac799d73
--- /dev/null
+++ b/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp
@@ -0,0 +1,794 @@
+/****************************************************************************
+**
+** 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 <qtimer.h>
+#include <qthread.h>
+
+#if defined Q_OS_UNIX
+#include <unistd.h>
+#endif
+
+#include "../../../../shared/util.h"
+
+
+//TESTED_CLASS=
+//TESTED_FILES=
+
+class tst_QTimer : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QTimer();
+ virtual ~tst_QTimer();
+
+
+public slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+private slots:
+ void zeroTimer();
+ void singleShotTimeout();
+ void timeout();
+ void livelock_data();
+ void livelock();
+ void timerInfiniteRecursion_data();
+ void timerInfiniteRecursion();
+ void recurringTimer_data();
+ void recurringTimer();
+ void deleteLaterOnQTimer(); // long name, don't want to shadow QObject::deleteLater()
+ void moveToThread();
+ void restartedTimerFiresTooSoon();
+ void timerFiresOnlyOncePerProcessEvents_data();
+ void timerFiresOnlyOncePerProcessEvents();
+ void timerIdPersistsAfterThreadExit();
+ void cancelLongTimer();
+ void singleShotStaticFunctionZeroTimeout();
+ void recurseOnTimeoutAndStopTimer();
+
+ void QTBUG13633_dontBlockEvents();
+ void postedEventsShouldNotStarveTimers();
+#ifdef Q_OS_SYMBIAN
+ void handleLeaks();
+#endif
+};
+
+class TimerHelper : public QObject
+{
+ Q_OBJECT
+public:
+ TimerHelper() : QObject(), count(0)
+ {
+ }
+
+ int count;
+
+public slots:
+ void timeout();
+};
+
+void TimerHelper::timeout()
+{
+ ++count;
+}
+
+tst_QTimer::tst_QTimer()
+{
+}
+
+tst_QTimer::~tst_QTimer()
+{
+}
+
+void tst_QTimer::initTestCase()
+{
+}
+
+void tst_QTimer::cleanupTestCase()
+{
+}
+
+void tst_QTimer::init()
+{
+}
+
+void tst_QTimer::cleanup()
+{
+}
+
+void tst_QTimer::zeroTimer()
+{
+ TimerHelper helper;
+ QTimer timer;
+ timer.setInterval(0);
+ timer.start();
+
+ connect(&timer, SIGNAL(timeout()), &helper, SLOT(timeout()));
+
+ QCoreApplication::processEvents();
+
+ QCOMPARE(helper.count, 1);
+}
+
+void tst_QTimer::singleShotTimeout()
+{
+ TimerHelper helper;
+ QTimer timer;
+ timer.setSingleShot(true);
+
+ connect(&timer, SIGNAL(timeout()), &helper, SLOT(timeout()));
+ timer.start(100);
+
+ QTest::qWait(500);
+ QCOMPARE(helper.count, 1);
+ QTest::qWait(500);
+ QCOMPARE(helper.count, 1);
+}
+
+#if defined(Q_OS_SYMBIAN)
+// Increase wait as emulator startup can cause unexpected delays, and
+// on hardware there are sometimes spikes right after process startup.
+#define TIMEOUT_TIMEOUT 2000
+#else
+#define TIMEOUT_TIMEOUT 200
+#endif
+
+void tst_QTimer::timeout()
+{
+ TimerHelper helper;
+ QTimer timer;
+
+ connect(&timer, SIGNAL(timeout()), &helper, SLOT(timeout()));
+ timer.start(100);
+
+ QCOMPARE(helper.count, 0);
+
+ QTest::qWait(TIMEOUT_TIMEOUT);
+ QVERIFY(helper.count > 0);
+ int oldCount = helper.count;
+
+ QTest::qWait(TIMEOUT_TIMEOUT);
+ QVERIFY(helper.count > oldCount);
+}
+
+
+void tst_QTimer::livelock_data()
+{
+ QTest::addColumn<int>("interval");
+ QTest::newRow("zero timer") << 0;
+ QTest::newRow("non-zero timer") << 1;
+ QTest::newRow("longer than sleep") << 20;
+}
+
+/*!
+ *
+ * DO NOT "FIX" THIS TEST! it is written like this for a reason, do
+ * not *change it without first dicussing it with its maintainers.
+ *
+*/
+class LiveLockTester : public QObject
+{
+public:
+ LiveLockTester(int i)
+ : interval(i),
+ timeoutsForFirst(0), timeoutsForExtra(0), timeoutsForSecond(0),
+ postEventAtRightTime(false)
+ {
+ firstTimerId = startTimer(interval);
+ extraTimerId = startTimer(interval + 80);
+ secondTimerId = -1; // started later
+ }
+
+ bool event(QEvent *e) {
+ if (e->type() == 4002) {
+ // got the posted event
+ if (timeoutsForFirst == 1 && timeoutsForSecond == 0)
+ postEventAtRightTime = true;
+ return true;
+ }
+ return QObject::event(e);
+ }
+
+ void timerEvent(QTimerEvent *te) {
+ if (te->timerId() == firstTimerId) {
+ if (++timeoutsForFirst == 1) {
+ killTimer(extraTimerId);
+ extraTimerId = -1;
+ QCoreApplication::postEvent(this, new QEvent(static_cast<QEvent::Type>(4002)));
+ secondTimerId = startTimer(interval);
+ }
+ } else if (te->timerId() == secondTimerId) {
+ ++timeoutsForSecond;
+ } else if (te->timerId() == extraTimerId) {
+ ++timeoutsForExtra;
+ }
+
+ // sleep for 2ms
+ QTest::qSleep(2);
+ killTimer(te->timerId());
+ }
+
+ const int interval;
+ int firstTimerId;
+ int secondTimerId;
+ int extraTimerId;
+ int timeoutsForFirst;
+ int timeoutsForExtra;
+ int timeoutsForSecond;
+ bool postEventAtRightTime;
+};
+
+void tst_QTimer::livelock()
+{
+ /*
+ New timers created in timer event handlers should not be sent
+ until the next iteration of the eventloop. Note: this test
+ depends on the fact that we send posted events before timer
+ events (since new posted events are not sent until the next
+ iteration of the eventloop either).
+ */
+ QFETCH(int, interval);
+ LiveLockTester tester(interval);
+ QTest::qWait(180); // we have to use wait here, since we're testing timers with a non-zero timeout
+ QTRY_COMPARE(tester.timeoutsForFirst, 1);
+ QCOMPARE(tester.timeoutsForExtra, 0);
+ QTRY_COMPARE(tester.timeoutsForSecond, 1);
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+ if (QSysInfo::WindowsVersion < QSysInfo::WV_XP)
+ QEXPECT_FAIL("non-zero timer", "Multimedia timers are not available on Windows 2000", Continue);
+#elif defined(Q_OS_WINCE)
+ QEXPECT_FAIL("non-zero timer", "Windows CE devices often too slow", Continue);
+#endif
+ QVERIFY(tester.postEventAtRightTime);
+}
+
+class TimerInfiniteRecursionObject : public QObject
+{
+public:
+ bool inTimerEvent;
+ bool timerEventRecursed;
+ int interval;
+
+ TimerInfiniteRecursionObject(int interval)
+ : inTimerEvent(false), timerEventRecursed(false), interval(interval)
+ { }
+
+ void timerEvent(QTimerEvent *timerEvent)
+ {
+ timerEventRecursed = inTimerEvent;
+ if (timerEventRecursed) {
+ // bug detected!
+ return;
+ }
+
+ inTimerEvent = true;
+
+ QEventLoop eventLoop;
+ QTimer::singleShot(qMax(100, interval * 2), &eventLoop, SLOT(quit()));
+ eventLoop.exec();
+
+ inTimerEvent = false;
+
+ killTimer(timerEvent->timerId());
+ }
+};
+
+void tst_QTimer::timerInfiniteRecursion_data()
+{
+ QTest::addColumn<int>("interval");
+ QTest::newRow("zero timer") << 0;
+ QTest::newRow("non-zero timer") << 1;
+ QTest::newRow("10ms timer") << 10;
+ QTest::newRow("11ms timer") << 11;
+ QTest::newRow("100ms timer") << 100;
+ QTest::newRow("1s timer") << 1000;
+}
+
+
+void tst_QTimer::timerInfiniteRecursion()
+{
+ QFETCH(int, interval);
+ TimerInfiniteRecursionObject object(interval);
+ (void) object.startTimer(interval);
+
+ QEventLoop eventLoop;
+ QTimer::singleShot(qMax(100, interval * 2), &eventLoop, SLOT(quit()));
+ eventLoop.exec();
+
+ QVERIFY(!object.timerEventRecursed);
+}
+
+class RecurringTimerObject : public QObject
+{
+Q_OBJECT
+public:
+ int times;
+ int target;
+ bool recurse;
+
+ RecurringTimerObject(int target)
+ : times(0), target(target), recurse(false)
+ { }
+
+ void timerEvent(QTimerEvent *timerEvent)
+ {
+ if (++times == target) {
+ killTimer(timerEvent->timerId());
+ emit done();
+ } if (recurse) {
+ QEventLoop eventLoop;
+ QTimer::singleShot(100, &eventLoop, SLOT(quit()));
+ eventLoop.exec();
+ }
+ }
+
+signals:
+ void done();
+};
+
+void tst_QTimer::recurringTimer_data()
+{
+ QTest::addColumn<int>("interval");
+ QTest::newRow("zero timer") << 0;
+ QTest::newRow("non-zero timer") << 1;
+}
+
+void tst_QTimer::recurringTimer()
+{
+ const int target = 5;
+ QFETCH(int, interval);
+
+ {
+ RecurringTimerObject object(target);
+ QObject::connect(&object, SIGNAL(done()), &QTestEventLoop::instance(), SLOT(exitLoop()));
+ (void) object.startTimer(interval);
+ QTestEventLoop::instance().enterLoop(5);
+
+ QCOMPARE(object.times, target);
+ }
+
+ {
+ // make sure that eventloop recursion doesn't effect timer recurrance
+ RecurringTimerObject object(target);
+ object.recurse = true;
+
+ QObject::connect(&object, SIGNAL(done()), &QTestEventLoop::instance(), SLOT(exitLoop()));
+ (void) object.startTimer(interval);
+ QTestEventLoop::instance().enterLoop(5);
+
+ QCOMPARE(object.times, target);
+ }
+}
+
+void tst_QTimer::deleteLaterOnQTimer()
+{
+ QTimer *timer = new QTimer;
+ connect(timer, SIGNAL(timeout()), timer, SLOT(deleteLater()));
+ connect(timer, SIGNAL(destroyed()), &QTestEventLoop::instance(), SLOT(exitLoop()));
+ timer->setInterval(1);
+ timer->setSingleShot(true);
+ timer->start();
+ QPointer<QTimer> pointer = timer;
+ QTestEventLoop::instance().enterLoop(5);
+ QVERIFY(pointer.isNull());
+}
+
+#if defined(Q_OS_SYMBIAN) && defined(Q_CC_NOKIAX86)
+// Increase wait as emulator startup can cause unexpected delays
+#define MOVETOTHREAD_TIMEOUT 200
+#define MOVETOTHREAD_WAIT 5000
+#else
+#define MOVETOTHREAD_TIMEOUT 200
+#define MOVETOTHREAD_WAIT 300
+#endif
+
+void tst_QTimer::moveToThread()
+{
+ QTimer ti1;
+ QTimer ti2;
+ ti1.start(MOVETOTHREAD_TIMEOUT);
+ ti2.start(MOVETOTHREAD_TIMEOUT);
+ QVERIFY((ti1.timerId() & 0xffffff) != (ti2.timerId() & 0xffffff));
+ QThread tr;
+ ti1.moveToThread(&tr);
+ connect(&ti1,SIGNAL(timeout()), &tr, SLOT(quit()));
+ tr.start();
+ QTimer ti3;
+ ti3.start(MOVETOTHREAD_TIMEOUT);
+ QVERIFY((ti3.timerId() & 0xffffff) != (ti2.timerId() & 0xffffff));
+ QVERIFY((ti3.timerId() & 0xffffff) != (ti1.timerId() & 0xffffff));
+ QTest::qWait(MOVETOTHREAD_WAIT);
+ QVERIFY(tr.wait());
+ ti2.stop();
+ QTimer ti4;
+ ti4.start(MOVETOTHREAD_TIMEOUT);
+ ti3.stop();
+ ti2.start(MOVETOTHREAD_TIMEOUT);
+ ti3.start(MOVETOTHREAD_TIMEOUT);
+ QVERIFY((ti4.timerId() & 0xffffff) != (ti2.timerId() & 0xffffff));
+ QVERIFY((ti3.timerId() & 0xffffff) != (ti2.timerId() & 0xffffff));
+ QVERIFY((ti3.timerId() & 0xffffff) != (ti1.timerId() & 0xffffff));
+}
+
+class RestartedTimerFiresTooSoonObject : public QObject
+{
+ Q_OBJECT
+
+public:
+ QBasicTimer m_timer;
+
+ int m_interval;
+ QTime m_startedTime;
+ QEventLoop eventLoop;
+
+ inline RestartedTimerFiresTooSoonObject()
+ : QObject(), m_interval(0)
+ { }
+
+ void timerFired()
+ {
+ static int interval = 1000;
+
+ m_interval = interval;
+ m_startedTime.start();
+ m_timer.start(interval, this);
+
+ // alternate between single-shot and 1 sec
+ interval = interval ? 0 : 1000;
+ }
+
+ void timerEvent(QTimerEvent* ev)
+ {
+ if (ev->timerId() != m_timer.timerId())
+ return;
+
+ m_timer.stop();
+
+ QTime now = QTime::currentTime();
+ int elapsed = m_startedTime.elapsed();
+
+ if (elapsed < m_interval / 2) {
+ // severely too early!
+ m_timer.stop();
+ eventLoop.exit(-1);
+ return;
+ }
+
+ timerFired();
+
+ // don't do this forever
+ static int count = 0;
+ if (count++ > 20) {
+ m_timer.stop();
+ eventLoop.quit();
+ return;
+ }
+ }
+};
+
+void tst_QTimer::restartedTimerFiresTooSoon()
+{
+ RestartedTimerFiresTooSoonObject object;
+ object.timerFired();
+ QVERIFY(object.eventLoop.exec() == 0);
+}
+
+class LongLastingSlotClass : public QObject
+{
+ Q_OBJECT
+
+public:
+ LongLastingSlotClass(QTimer *timer) : count(0), timer(timer) {}
+
+public slots:
+ void longLastingSlot()
+ {
+ // Don't use timers for this, because we are testing them.
+ QTime time;
+ time.start();
+ while (time.elapsed() < 200) {
+ for (int c = 0; c < 100000; c++) {} // Mindless looping.
+ }
+ if (++count >= 2) {
+ timer->stop();
+ }
+ }
+
+public:
+ int count;
+ QTimer *timer;
+};
+
+void tst_QTimer::timerFiresOnlyOncePerProcessEvents_data()
+{
+ QTest::addColumn<int>("interval");
+ QTest::newRow("zero timer") << 0;
+ QTest::newRow("non-zero timer") << 10;
+}
+
+void tst_QTimer::timerFiresOnlyOncePerProcessEvents()
+{
+ QFETCH(int, interval);
+
+ QTimer t;
+ LongLastingSlotClass longSlot(&t);
+ t.start(interval);
+ connect(&t, SIGNAL(timeout()), &longSlot, SLOT(longLastingSlot()));
+ // Loop because there may be other events pending.
+ while (longSlot.count == 0) {
+ QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents);
+ }
+
+ QCOMPARE(longSlot.count, 1);
+}
+
+class TimerIdPersistsAfterThreadExitThread : public QThread
+{
+public:
+ QTimer *timer;
+ int timerId, returnValue;
+
+ TimerIdPersistsAfterThreadExitThread()
+ : QThread(), timer(0), timerId(-1), returnValue(-1)
+ { }
+ ~TimerIdPersistsAfterThreadExitThread()
+ {
+ delete timer;
+ }
+
+ void run()
+ {
+ QEventLoop eventLoop;
+ timer = new QTimer;
+ connect(timer, SIGNAL(timeout()), &eventLoop, SLOT(quit()));
+ timer->start(100);
+ timerId = timer->timerId();
+ returnValue = eventLoop.exec();
+ }
+};
+
+void tst_QTimer::timerIdPersistsAfterThreadExit()
+{
+ TimerIdPersistsAfterThreadExitThread thread;
+ thread.start();
+ QVERIFY(thread.wait(30000));
+ QCOMPARE(thread.returnValue, 0);
+
+ // even though the thread has exited, and the event dispatcher destroyed, the timer is still
+ // "active", meaning the timer id should NOT be reused (i.e. the event dispatcher should not
+ // have unregistered it)
+ int timerId = thread.startTimer(100);
+ QVERIFY((timerId & 0xffffff) != (thread.timerId & 0xffffff));
+}
+
+void tst_QTimer::cancelLongTimer()
+{
+ QTimer timer;
+ timer.setSingleShot(true);
+ timer.start(1000 * 60 * 60); //set timer for 1 hour (which would overflow Symbian RTimer)
+ QCoreApplication::processEvents();
+ QVERIFY(timer.isActive()); //if the timer completes immediately with an error, then this will fail
+ timer.stop();
+ QVERIFY(!timer.isActive());
+}
+
+void tst_QTimer::singleShotStaticFunctionZeroTimeout()
+{
+ TimerHelper helper;
+
+ QTimer::singleShot(0, &helper, SLOT(timeout()));
+ QTest::qWait(500);
+ QCOMPARE(helper.count, 1);
+ QTest::qWait(500);
+ QCOMPARE(helper.count, 1);
+}
+
+class RecursOnTimeoutAndStopTimerTimer : public QObject
+{
+ Q_OBJECT
+
+public:
+ QTimer *one;
+ QTimer *two;
+
+public slots:
+ void onetrigger()
+ {
+ QCoreApplication::processEvents();
+ }
+
+ void twotrigger()
+ {
+ one->stop();
+ }
+};
+
+void tst_QTimer::recurseOnTimeoutAndStopTimer()
+{
+ QEventLoop eventLoop;
+ QTimer::singleShot(1000, &eventLoop, SLOT(quit()));
+
+ RecursOnTimeoutAndStopTimerTimer t;
+ t.one = new QTimer(&t);
+ t.two = new QTimer(&t);
+
+ QObject::connect(t.one, SIGNAL(timeout()), &t, SLOT(onetrigger()));
+ QObject::connect(t.two, SIGNAL(timeout()), &t, SLOT(twotrigger()));
+
+ t.two->setSingleShot(true);
+
+ t.one->start();
+ t.two->start();
+
+ (void) eventLoop.exec();
+
+ QVERIFY(!t.one->isActive());
+ QVERIFY(!t.two->isActive());
+}
+
+
+
+class DontBlockEvents : public QObject
+{
+ Q_OBJECT
+public:
+ DontBlockEvents();
+ void timerEvent(QTimerEvent*);
+
+ int count;
+ int total;
+ QBasicTimer m_timer;
+
+public slots:
+ void paintEvent();
+
+};
+
+DontBlockEvents::DontBlockEvents()
+{
+ count = 0;
+ total = 0;
+
+ //QTBUG-13633 need few unrelated timer running to reproduce the bug.
+ (new QTimer(this))->start(2000);
+ (new QTimer(this))->start(2500);
+ (new QTimer(this))->start(3000);
+ (new QTimer(this))->start(5000);
+ (new QTimer(this))->start(1000);
+ (new QTimer(this))->start(2000);
+
+ m_timer.start(1, this);
+}
+
+void DontBlockEvents::timerEvent(QTimerEvent* event)
+{
+ if (event->timerId() == m_timer.timerId()) {
+ QMetaObject::invokeMethod(this, "paintEvent", Qt::QueuedConnection);
+ m_timer.start(0, this);
+ count++;
+ QCOMPARE(count, 1);
+ total++;
+ }
+}
+
+void DontBlockEvents::paintEvent()
+{
+ count--;
+ QCOMPARE(count, 0);
+}
+
+
+void tst_QTimer::QTBUG13633_dontBlockEvents()
+{
+ DontBlockEvents t;
+ QTest::qWait(60);
+ QTRY_VERIFY(t.total > 2);
+}
+
+class SlotRepeater : public QObject {
+ Q_OBJECT
+public:
+ SlotRepeater() {}
+
+public slots:
+ void repeatThisSlot()
+ {
+ QMetaObject::invokeMethod(this, "repeatThisSlot", Qt::QueuedConnection);
+ }
+};
+
+void tst_QTimer::postedEventsShouldNotStarveTimers()
+{
+ TimerHelper timerHelper;
+ QTimer timer;
+ connect(&timer, SIGNAL(timeout()), &timerHelper, SLOT(timeout()));
+ timer.setInterval(0);
+ timer.setSingleShot(false);
+ timer.start();
+ SlotRepeater slotRepeater;
+ slotRepeater.repeatThisSlot();
+ QTest::qWait(100);
+ QVERIFY(timerHelper.count > 5);
+}
+
+#ifdef Q_OS_SYMBIAN
+void tst_QTimer::handleLeaks()
+{
+ const int timercount = 5;
+ int processhandles_start;
+ int threadhandles_start;
+ RThread().HandleCount(processhandles_start, threadhandles_start);
+ {
+ TimerHelper timerHelper;
+ QList<QTimer*> timers;
+ for (int i=0;i<timercount;i++) {
+ QTimer* timer = new QTimer;
+ timers.append(timer);
+ connect(timer, SIGNAL(timeout()), &timerHelper, SLOT(timeout()));
+ timer->setSingleShot(true);
+ timer->start(i); //test both zero and normal timeouts
+ }
+ int processhandles_mid;
+ int threadhandles_mid;
+ RThread().HandleCount(processhandles_mid, threadhandles_mid);
+ qDebug() << threadhandles_mid - threadhandles_start << "new thread owned handles";
+ QTest::qWait(100);
+ QCOMPARE(timerHelper.count, timercount);
+ qDeleteAll(timers);
+ }
+ int processhandles_end;
+ int threadhandles_end;
+ RThread().HandleCount(processhandles_end, threadhandles_end);
+ QCOMPARE(threadhandles_end, threadhandles_start); //RTimer::CreateLocal creates a thread owned handle
+ //Can not verify process handles because QObject::connect may create up to 2 mutexes
+ //from a QMutexPool (4 process owned handles with open C imp.)
+ //QCOMPARE(processhandles_end, processhandles_start);
+}
+#endif
+
+QTEST_MAIN(tst_QTimer)
+#include "tst_qtimer.moc"
diff --git a/tests/auto/corelib/kernel/qtipc/lackey/lackey.pro b/tests/auto/corelib/kernel/qtipc/lackey/lackey.pro
new file mode 100644
index 0000000000..91659d701e
--- /dev/null
+++ b/tests/auto/corelib/kernel/qtipc/lackey/lackey.pro
@@ -0,0 +1,16 @@
+include(../qsharedmemory/src/src.pri)
+
+QT = core-private script testlib
+
+DESTDIR = ./
+
+win32: CONFIG += console
+mac:CONFIG -= app_bundle
+
+DEFINES += QSHAREDMEMORY_DEBUG
+DEFINES += QSYSTEMSEMAPHORE_DEBUG
+
+SOURCES += main.cpp
+TARGET = lackey
+
+
diff --git a/tests/auto/corelib/kernel/qtipc/lackey/main.cpp b/tests/auto/corelib/kernel/qtipc/lackey/main.cpp
new file mode 100644
index 0000000000..3caae48740
--- /dev/null
+++ b/tests/auto/corelib/kernel/qtipc/lackey/main.cpp
@@ -0,0 +1,370 @@
+/****************************************************************************
+**
+** 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 <qscriptengine.h>
+
+#include <QtCore/QFile>
+#include <QtCore/QTextStream>
+#include <QTest>
+
+#include <qstringlist.h>
+#include <stdlib.h>
+#include <qsharedmemory.h>
+#include <qsystemsemaphore.h>
+#include <qsystemlock.h>
+
+class ScriptSystemSemaphore : public QObject
+{
+ Q_OBJECT
+
+public:
+ ScriptSystemSemaphore(QObject *parent = 0) : QObject(parent), ss(QString())
+ {
+ }
+
+public slots:
+ bool acquire()
+ {
+ return ss.acquire();
+ };
+
+ bool release(int n = 1)
+ {
+ return ss.release(n);
+ };
+
+ void setKey(const QString &key, int n = 0)
+ {
+ ss.setKey(key, n);
+ };
+
+ QString key() const
+ {
+ return ss.key();
+ }
+
+private:
+ QSystemSemaphore ss;
+};
+
+class ScriptSystemLock : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString key WRITE setKey READ key)
+
+public:
+ ScriptSystemLock(QObject *parent = 0) : QObject(parent), sl(QString())
+ {
+ }
+
+public slots:
+
+ bool lockReadOnly()
+ {
+ return sl.lock(QSystemLock::ReadOnly);
+ }
+
+ bool lock()
+ {
+ return sl.lock();
+ };
+
+ bool unlock()
+ {
+ return sl.unlock();
+ };
+
+ void setKey(const QString &key)
+ {
+ sl.setKey(key);
+ };
+
+ QString key() const
+ {
+ return sl.key();
+ }
+
+private:
+ QSystemLock sl;
+};
+
+class ScriptSharedMemory : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(bool attached READ isAttached)
+ Q_PROPERTY(QString key WRITE setKey READ key)
+
+public:
+ enum SharedMemoryError
+ {
+ NoError = 0,
+ PermissionDenied = 1,
+ InvalidSize = 2,
+ KeyError = 3,
+ AlreadyExists = 4,
+ NotFound = 5,
+ LockError = 6,
+ OutOfResources = 7,
+ UnknownError = 8
+ };
+
+ ScriptSharedMemory(QObject *parent = 0) : QObject(parent)
+ {
+ }
+
+public slots:
+ void sleep(int x) const
+ {
+ QTest::qSleep(x);
+ }
+
+ bool create(int size)
+ {
+ return sm.create(size);
+ };
+
+ bool createReadOnly(int size)
+ {
+ return sm.create(size, QSharedMemory::ReadOnly);
+ };
+
+ int size() const
+ {
+ return sm.size();
+ };
+
+ bool attach()
+ {
+ return sm.attach();
+ };
+
+ bool attachReadOnly()
+ {
+ return sm.attach(QSharedMemory::ReadOnly);
+ };
+
+ bool isAttached() const
+ {
+ return sm.isAttached();
+ };
+
+ bool detach()
+ {
+ return sm.detach();
+ };
+
+ int error() const
+ {
+ return (int)sm.error();
+ };
+
+ QString errorString() const
+ {
+ return sm.errorString();
+ };
+
+ void set(int i, QChar value)
+ {
+ ((char*)sm.data())[i] = value.toLatin1();
+ }
+
+ QString get(int i)
+ {
+ return QChar::fromLatin1(((char*)sm.data())[i]);
+ }
+
+ char *data() const
+ {
+ return (char*)sm.data();
+ };
+
+ void setKey(const QString &key)
+ {
+ sm.setKey(key);
+ };
+
+ QString key() const
+ {
+ return sm.key();
+ }
+
+ bool lock()
+ {
+ return sm.lock();
+ }
+
+ bool unlock()
+ {
+ return sm.unlock();
+ }
+
+private:
+ QSharedMemory sm;
+};
+
+QT_BEGIN_NAMESPACE
+Q_SCRIPT_DECLARE_QMETAOBJECT(ScriptSharedMemory, QObject*);
+Q_SCRIPT_DECLARE_QMETAOBJECT(ScriptSystemLock, QObject*);
+Q_SCRIPT_DECLARE_QMETAOBJECT(ScriptSystemSemaphore, QObject*);
+QT_END_NAMESPACE
+
+static void interactive(QScriptEngine &eng)
+{
+#ifdef Q_OS_WINCE
+ fprintf(stderr, "Interactive mode not supported on Windows CE\n");
+ return;
+#endif
+ QTextStream qin(stdin, QFile::ReadOnly);
+
+ const char *qscript_prompt = "qs> ";
+ const char *dot_prompt = ".... ";
+ const char *prompt = qscript_prompt;
+
+ QString code;
+
+ forever {
+ QString line;
+
+ printf("%s", prompt);
+ fflush(stdout);
+
+ line = qin.readLine();
+ if (line.isNull())
+ break;
+
+ code += line;
+ code += QLatin1Char('\n');
+
+ if (line.trimmed().isEmpty()) {
+ continue;
+
+ } else if (! eng.canEvaluate(code)) {
+ prompt = dot_prompt;
+
+ } else {
+ QScriptValue result = eng.evaluate(code);
+ code.clear();
+ prompt = qscript_prompt;
+ if (!result.isUndefined())
+ fprintf(stderr, "%s\n", qPrintable(result.toString()));
+ }
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication app(argc, argv);
+
+ QScriptEngine eng;
+ QScriptValue globalObject = eng.globalObject();
+
+ QScriptValue sm = qScriptValueFromQMetaObject<ScriptSharedMemory>(&eng);
+ eng.globalObject().setProperty("ScriptSharedMemory", sm);
+
+ QScriptValue sl = qScriptValueFromQMetaObject<ScriptSystemLock>(&eng);
+ eng.globalObject().setProperty("ScriptSystemLock", sl);
+
+ QScriptValue ss = qScriptValueFromQMetaObject<ScriptSystemSemaphore>(&eng);
+ eng.globalObject().setProperty("ScriptSystemSemaphore", ss);
+
+
+ if (! *++argv) {
+ interactive(eng);
+ return EXIT_SUCCESS;
+ }
+
+ QStringList arguments = app.arguments();
+ arguments.takeFirst();
+
+ while (!arguments.isEmpty()) {
+ QString fn = arguments.takeFirst();
+
+ if (fn == QLatin1String("-i")) {
+ interactive(eng);
+ break;
+ }
+
+ QString contents;
+
+ if (fn == QLatin1String("-")) {
+ QTextStream stream(stdin, QFile::ReadOnly);
+ contents = stream.readAll();
+ } else {
+ QFile file(fn);
+ if (!file.exists()) {
+ fprintf(stderr, "%s doesn't exists\n", qPrintable(fn));
+ return EXIT_FAILURE;
+ }
+ if (file.open(QFile::ReadOnly)) {
+ QTextStream stream(&file);
+ contents = stream.readAll();
+ file.close();
+ }
+ }
+
+ if (contents.isEmpty())
+ continue;
+
+ if (contents[0] == '#') {
+ contents.prepend("//");
+ QScriptValue args = eng.newArray();
+ args.setProperty("0", QScriptValue(&eng, fn));
+ int i = 1;
+ while (!arguments.isEmpty())
+ args.setProperty(i++, QScriptValue(&eng, arguments.takeFirst()));
+ eng.currentContext()->activationObject().setProperty("args", args);
+ }
+ QScriptValue r = eng.evaluate(contents);
+ if (eng.hasUncaughtException()) {
+ int line = eng.uncaughtExceptionLineNumber();
+ fprintf(stderr, "%d: %s\n\t%s\n\n", line, qPrintable(fn), qPrintable(r.toString()));
+ return EXIT_FAILURE;
+ }
+ if (r.isNumber())
+ return r.toInt32();
+ }
+
+ return EXIT_SUCCESS;
+}
+
+#include "main.moc"
diff --git a/tests/auto/corelib/kernel/qtipc/lackey/scripts/consumer.js b/tests/auto/corelib/kernel/qtipc/lackey/scripts/consumer.js
new file mode 100644
index 0000000000..4d12dcac1a
--- /dev/null
+++ b/tests/auto/corelib/kernel/qtipc/lackey/scripts/consumer.js
@@ -0,0 +1,41 @@
+function QVERIFY(x, debugInfo) {
+ if (!(x)) {
+ print(debugInfo);
+ throw(debugInfo);
+ }
+}
+
+var consumer = new ScriptSharedMemory;
+consumer.setKey("market");
+
+//print("consumer starting");
+var tries = 0;;
+while(!consumer.attach()) {
+ if (tries == 5000) {
+ var message = "consumer exiting, waiting too long";
+ print(message);
+ throw(message);
+ }
+ ++tries;
+ consumer.sleep(1);
+}
+//print("consumer attached");
+
+
+var i = 0;
+while(true) {
+ QVERIFY(consumer.lock(), "lock");
+ if (consumer.get(0) == 'Q') {
+ consumer.set(0, ++i);
+ //print ("consumer sets" + i);
+ }
+ if (consumer.get(0) == 'E') {
+ QVERIFY(consumer.unlock(), "unlock");
+ break;
+ }
+ QVERIFY(consumer.unlock(), "unlock");
+ consumer.sleep(10);
+}
+
+//print("consumer detaching");
+QVERIFY(consumer.detach());
diff --git a/tests/auto/corelib/kernel/qtipc/lackey/scripts/producer.js b/tests/auto/corelib/kernel/qtipc/lackey/scripts/producer.js
new file mode 100644
index 0000000000..e02cd8b7bb
--- /dev/null
+++ b/tests/auto/corelib/kernel/qtipc/lackey/scripts/producer.js
@@ -0,0 +1,44 @@
+function QVERIFY(x, debugInfo) {
+ if (!(x)) {
+ print(debugInfo);
+ throw(debugInfo);
+ }
+}
+
+var producer = new ScriptSharedMemory;
+producer.setKey("market");
+
+var size = 1024;
+if (!producer.create(size)) {
+ QVERIFY(producer.error() == 4, "create");
+ QVERIFY(producer.attach());
+}
+//print ("producer created and attached");
+
+QVERIFY(producer.lock());
+producer.set(0, 'Q');
+QVERIFY(producer.unlock());
+
+var i = 0;
+while(i < 5) {
+ QVERIFY(producer.lock(), "lock");
+ if (producer.get(0) == 'Q') {
+ QVERIFY(producer.unlock(), "unlock");
+ producer.sleep(1);
+ continue;
+ }
+ //print("producer: " + i);
+ ++i;
+ producer.set(0, 'Q');
+ QVERIFY(producer.unlock(), "unlock");
+ producer.sleep(1);
+}
+QVERIFY(producer.lock());
+producer.set(0, 'E');
+QVERIFY(producer.unlock());
+
+//print ("producer done");
+
+// Sleep for a bit to let all consumers start, otherwise they will get stuck in the attach loop,
+// because at least in Symbian the shared memory will be destroyed if there are no active handles to it.
+producer.sleep(3000); \ No newline at end of file
diff --git a/tests/auto/corelib/kernel/qtipc/lackey/scripts/readonly_segfault.js b/tests/auto/corelib/kernel/qtipc/lackey/scripts/readonly_segfault.js
new file mode 100644
index 0000000000..3eaf789d23
--- /dev/null
+++ b/tests/auto/corelib/kernel/qtipc/lackey/scripts/readonly_segfault.js
@@ -0,0 +1,4 @@
+var sm = new ScriptSharedMemory;
+sm.setKey("readonly_segfault");
+sm.createReadOnly(1024);
+var data = sm.set(0, "a");
diff --git a/tests/auto/corelib/kernel/qtipc/lackey/scripts/systemlock_read.js b/tests/auto/corelib/kernel/qtipc/lackey/scripts/systemlock_read.js
new file mode 100644
index 0000000000..1048bc7955
--- /dev/null
+++ b/tests/auto/corelib/kernel/qtipc/lackey/scripts/systemlock_read.js
@@ -0,0 +1,11 @@
+function QVERIFY(x, debugInfo) {
+ if (!(x)) {
+ print(debugInfo);
+ throw(debugInfo);
+ }
+}
+
+var lock = new ScriptSystemLock;
+lock.setKey("market");
+QVERIFY(lock.lockReadOnly());
+QVERIFY(lock.unlock()); \ No newline at end of file
diff --git a/tests/auto/corelib/kernel/qtipc/lackey/scripts/systemlock_readwrite.js b/tests/auto/corelib/kernel/qtipc/lackey/scripts/systemlock_readwrite.js
new file mode 100644
index 0000000000..fc6367ff4b
--- /dev/null
+++ b/tests/auto/corelib/kernel/qtipc/lackey/scripts/systemlock_readwrite.js
@@ -0,0 +1,11 @@
+function QVERIFY(x, debugInfo) {
+ if (!(x)) {
+ print(debugInfo);
+ throw(debugInfo);
+ }
+}
+
+var lock = new ScriptSystemLock;
+lock.setKey("market");
+QVERIFY(lock.lock());
+QVERIFY(lock.unlock());
diff --git a/tests/auto/corelib/kernel/qtipc/lackey/scripts/systemsemaphore_acquire.js b/tests/auto/corelib/kernel/qtipc/lackey/scripts/systemsemaphore_acquire.js
new file mode 100644
index 0000000000..5cff429b86
--- /dev/null
+++ b/tests/auto/corelib/kernel/qtipc/lackey/scripts/systemsemaphore_acquire.js
@@ -0,0 +1,18 @@
+#/bin/qscript
+function QVERIFY(x, debugInfo) {
+ if (!(x)) {
+ print(debugInfo);
+ throw(debugInfo);
+ }
+}
+
+
+var sem = new ScriptSystemSemaphore;
+sem.setKey("store");
+
+var count = Number(args[1]);
+if (isNaN(count))
+ count = 1;
+for (var i = 0; i < count; ++i)
+ QVERIFY(sem.acquire());
+print("done aquiring");
diff --git a/tests/auto/corelib/kernel/qtipc/lackey/scripts/systemsemaphore_acquirerelease.js b/tests/auto/corelib/kernel/qtipc/lackey/scripts/systemsemaphore_acquirerelease.js
new file mode 100644
index 0000000000..cedde3f0ae
--- /dev/null
+++ b/tests/auto/corelib/kernel/qtipc/lackey/scripts/systemsemaphore_acquirerelease.js
@@ -0,0 +1,11 @@
+function QVERIFY(x, debugInfo) {
+ if (!(x)) {
+ print(debugInfo);
+ throw(debugInfo);
+ }
+}
+
+var lock = new ScriptSystemSemaphore;
+lock.setKey("store");
+QVERIFY(lock.acquire());
+QVERIFY(lock.release());
diff --git a/tests/auto/corelib/kernel/qtipc/lackey/scripts/systemsemaphore_release.js b/tests/auto/corelib/kernel/qtipc/lackey/scripts/systemsemaphore_release.js
new file mode 100644
index 0000000000..c805e0fed8
--- /dev/null
+++ b/tests/auto/corelib/kernel/qtipc/lackey/scripts/systemsemaphore_release.js
@@ -0,0 +1,11 @@
+function QVERIFY(x, debugInfo) {
+ if (!(x)) {
+ print(debugInfo);
+ throw(debugInfo);
+ }
+}
+
+var sem = new ScriptSystemSemaphore;
+sem.setKey("store");
+QVERIFY(sem.release());
+print ("done releasing");
diff --git a/tests/auto/corelib/kernel/qtipc/qsharedmemory/.gitignore b/tests/auto/corelib/kernel/qtipc/qsharedmemory/.gitignore
new file mode 100644
index 0000000000..03ddcf29cf
--- /dev/null
+++ b/tests/auto/corelib/kernel/qtipc/qsharedmemory/.gitignore
@@ -0,0 +1,3 @@
+tst_qsharedmemory
+lackey/lackey.exe
+qsystemlock/tst_qsystemlock.exe
diff --git a/tests/auto/corelib/kernel/qtipc/qsharedmemory/qsharedmemory.pro b/tests/auto/corelib/kernel/qtipc/qsharedmemory/qsharedmemory.pro
new file mode 100644
index 0000000000..9fef8e4112
--- /dev/null
+++ b/tests/auto/corelib/kernel/qtipc/qsharedmemory/qsharedmemory.pro
@@ -0,0 +1,4 @@
+TEMPLATE = subdirs
+SUBDIRS = test qsystemlock
+
+
diff --git a/tests/auto/corelib/kernel/qtipc/qsharedmemory/qsystemlock/qsystemlock.pro b/tests/auto/corelib/kernel/qtipc/qsharedmemory/qsystemlock/qsystemlock.pro
new file mode 100644
index 0000000000..e3d99bb85c
--- /dev/null
+++ b/tests/auto/corelib/kernel/qtipc/qsharedmemory/qsystemlock/qsystemlock.pro
@@ -0,0 +1,22 @@
+CONFIG += qttest_p4
+QT += gui-private
+
+include(../src/src.pri)
+win32: CONFIG += console
+mac:CONFIG -= app_bundle
+
+wince* {
+ DEFINES += SRCDIR=\\\"\\\"
+} else:!symbian {
+ DEFINES += SRCDIR=\\\"$$PWD\\\"
+}
+
+DESTDIR = ./
+
+DEFINES += QSHAREDMEMORY_DEBUG
+DEFINES += QSYSTEMSEMAPHORE_DEBUG
+
+SOURCES += tst_qsystemlock.cpp
+TARGET = tst_qsystemlock
+
+
diff --git a/tests/auto/corelib/kernel/qtipc/qsharedmemory/qsystemlock/tst_qsystemlock.cpp b/tests/auto/corelib/kernel/qtipc/qsharedmemory/qsystemlock/tst_qsystemlock.cpp
new file mode 100644
index 0000000000..072b94f985
--- /dev/null
+++ b/tests/auto/corelib/kernel/qtipc/qsharedmemory/qsystemlock/tst_qsystemlock.cpp
@@ -0,0 +1,239 @@
+/****************************************************************************
+**
+** 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 <qsystemlock.h>
+
+//TESTED_CLASS=
+//TESTED_FILES=
+
+#ifdef Q_OS_SYMBIAN
+// In Symbian OS test data is located in applications private dir
+// And underlying Open C have application private dir in default search path
+#define SRCDIR ""
+#endif
+
+
+#define EXISTING_SHARE "existing"
+
+class tst_QSystemLock : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QSystemLock();
+ virtual ~tst_QSystemLock();
+
+public Q_SLOTS:
+ void init();
+ void cleanup();
+
+private slots:
+ void key_data();
+ void key();
+
+ void basicLock();
+ void complexLock();
+ void lockModes();
+ void sucessive();
+ void processes_data();
+ void processes();
+
+private:
+ QSystemLock *existingLock;
+
+};
+
+tst_QSystemLock::tst_QSystemLock()
+{
+}
+
+tst_QSystemLock::~tst_QSystemLock()
+{
+}
+
+void tst_QSystemLock::init()
+{
+ existingLock = new QSystemLock(EXISTING_SHARE);
+}
+
+void tst_QSystemLock::cleanup()
+{
+ delete existingLock;
+}
+
+void tst_QSystemLock::key_data()
+{
+ QTest::addColumn<QString>("constructorKey");
+ QTest::addColumn<QString>("setKey");
+
+ QTest::newRow("null, null") << QString() << QString();
+ QTest::newRow("null, one") << QString() << QString("one");
+ QTest::newRow("one, two") << QString("one") << QString("two");
+}
+
+/*!
+ Basic key testing
+ */
+void tst_QSystemLock::key()
+{
+ QFETCH(QString, constructorKey);
+ QFETCH(QString, setKey);
+
+ QSystemLock sl(constructorKey);
+ QCOMPARE(sl.key(), constructorKey);
+ sl.setKey(setKey);
+ QCOMPARE(sl.key(), setKey);
+}
+
+void tst_QSystemLock::basicLock()
+{
+ QSystemLock lock("foo");
+ QVERIFY(lock.lock());
+ QVERIFY(lock.unlock());
+}
+
+void tst_QSystemLock::complexLock()
+{
+ QSystemLock lock("foo");
+ QVERIFY(lock.lock(QSystemLock::ReadOnly));
+ QVERIFY(lock.unlock());
+
+ QVERIFY(lock.lock(QSystemLock::ReadWrite));
+ QVERIFY(lock.unlock());
+
+ QVERIFY(lock.lock(QSystemLock::ReadOnly));
+ QVERIFY(lock.lock(QSystemLock::ReadOnly));
+ QVERIFY(lock.unlock());
+ QVERIFY(lock.unlock());
+}
+
+void tst_QSystemLock::lockModes()
+{
+ QSystemLock reader1("library");
+ QSystemLock reader2("library");
+
+ QSystemLock librarian("library");
+ QVERIFY(reader1.lock(QSystemLock::ReadOnly));
+ QVERIFY(reader2.lock(QSystemLock::ReadOnly));
+ QVERIFY(reader1.unlock());
+ QVERIFY(reader2.unlock());
+ QVERIFY(librarian.lock(QSystemLock::ReadWrite));
+ QVERIFY(librarian.unlock());
+}
+
+void tst_QSystemLock::sucessive()
+{
+ QSystemLock lock("library");
+ QVERIFY(lock.lock(QSystemLock::ReadOnly));
+ QVERIFY(lock.lock(QSystemLock::ReadOnly));
+ QVERIFY(lock.lock(QSystemLock::ReadOnly));
+ QVERIFY(lock.lock(QSystemLock::ReadOnly));
+ QVERIFY(lock.lock(QSystemLock::ReadOnly));
+ QVERIFY(lock.unlock());
+ QVERIFY(lock.unlock());
+ QVERIFY(lock.unlock());
+ QVERIFY(lock.unlock());
+ QVERIFY(lock.unlock());
+ QVERIFY(!lock.unlock());
+}
+
+void tst_QSystemLock::processes_data()
+{
+ QTest::addColumn<int>("readOnly");
+ QTest::addColumn<int>("readWrite");
+ for (int i = 0; i < 5; ++i) {
+ QTest::newRow("1/0 process") << 1 << 0;
+ QTest::newRow("0/1 process") << 0 << 1;
+ QTest::newRow("0/4 process") << 0 << 4;
+ QTest::newRow("1/1 process") << 1 << 1;
+ QTest::newRow("10/1 process") << 10 << 1;
+ QTest::newRow("1/10 process") << 1 << 10;
+ QTest::newRow("10/10 processes") << 10 << 10;
+ }
+}
+
+/*!
+ Create external processes
+ */
+void tst_QSystemLock::processes()
+{
+ QSKIP("This test takes about 15 minutes and needs to be trimmed down before we can re-enable it", SkipAll);
+ QFETCH(int, readOnly);
+ QFETCH(int, readWrite);
+
+ QStringList scripts;
+ for (int i = 0; i < readOnly; ++i)
+ scripts.append(QFileInfo(SRCDIR "/../lackey/scripts/systemlock_read.js").absoluteFilePath() );
+ for (int i = 0; i < readWrite; ++i)
+ scripts.append(QFileInfo(SRCDIR "/../lackey/scripts/systemlock_readwrite.js").absoluteFilePath());
+
+ QList<QProcess*> consumers;
+ unsigned int failedProcesses = 0;
+ for (int i = 0; i < scripts.count(); ++i) {
+
+ QStringList arguments = QStringList() << scripts.at(i);
+ QProcess *p = new QProcess;
+ p->setProcessChannelMode(QProcess::ForwardedChannels);
+
+ p->start("../lackey/lackey", arguments);
+ // test, if the process could be started.
+
+ if (p->waitForStarted(2000))
+ consumers.append(p);
+ else
+ ++failedProcesses;
+ }
+
+ while (!consumers.isEmpty()) {
+ consumers.first()->waitForFinished(3000);
+ consumers.first()->kill();
+ QCOMPARE(consumers.first()->exitStatus(), QProcess::NormalExit);
+ QCOMPARE(consumers.first()->exitCode(), 0);
+ delete consumers.takeFirst();
+ }
+ QCOMPARE(failedProcesses, (unsigned int)(0));
+}
+
+QTEST_MAIN(tst_QSystemLock)
+#include "tst_qsystemlock.moc"
+
diff --git a/tests/auto/corelib/kernel/qtipc/qsharedmemory/src/qsystemlock.cpp b/tests/auto/corelib/kernel/qtipc/qsharedmemory/src/qsystemlock.cpp
new file mode 100644
index 0000000000..b48bd7bebe
--- /dev/null
+++ b/tests/auto/corelib/kernel/qtipc/qsharedmemory/src/qsystemlock.cpp
@@ -0,0 +1,246 @@
+/****************************************************************************
+**
+** 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 "qsystemlock.h"
+#include "qsystemlock_p.h"
+
+#include <qdebug.h>
+
+/*! \class QSystemLocker
+
+ \brief The QSystemLocker class is a convenience class that simplifies
+ locking and unlocking system locks.
+
+ The purpose of QSystemLocker is to simplify QSystemLock locking and
+ unlocking. Locking and unlocking a QSystemLock in complex functions and
+ statements or in exception handling code is error-prone and difficult to
+ debug. QSystemLocker can be used in such situations to ensure that the
+ state of the locks is always well-defined.
+
+ QSystemLocker should be created within a function where a QSystemLock needs
+ to be locked. The system lock is locked when QSystemLocker is created. If
+ locked, the system lock will be unlocked when the QSystemLocker is
+ destroyed. QSystemLocker can be unlocked with unlock() and relocked with
+ relock().
+
+ \sa QSystemLock
+ */
+
+/*! \fn QSystemLocker::QSystemLocker()
+
+ Constructs a QSystemLocker and locks \a lock. The \a lock will be
+ unlocked when the QSystemLocker is destroyed. If lock is zero,
+ QSystemLocker does nothing.
+
+ \sa QSystemLock::lock()
+ */
+
+/*! \fn QSystemLocker::~QSystemLocker()
+
+ Destroys the QSystemLocker and unlocks it if it was
+ locked in the constructor.
+
+ \sa QSystemLock::unlock()
+ */
+
+/*! \fn QSystemLocker::systemLock()
+
+ Returns a pointer to the lock that was locked in the constructor.
+ */
+
+/*! \fn QSystemLocker::relock()
+
+ Relocks an unlocked locker.
+
+ \sa unlock()
+ */
+
+/*! \fn QSystemLocker::unlock()
+
+ Unlocks this locker. You can use relock() to lock it again.
+ It does not need to be locked when destroyed.
+
+ \sa relock()
+ */
+
+/*! \class QSystemLock
+
+ \brief The QSystemLock class provides a system wide lock
+ that can be used between threads or processes.
+
+ The purpose of a QSystemLocker is to protect an object that can be
+ accessed by multiple threads or processes such as shared memory or a file.
+
+ For example, say there is a method which prints a message to a log file:
+
+ void log(const QString &logText)
+ {
+ QSystemLock systemLock(QLatin1String("logfile"));
+ systemLock.lock();
+ QFile file(QDir::temp() + QLatin1String("/log"));
+ if (file.open(QIODevice::Append)) {
+ QTextStream out(&file);
+ out << logText;
+ }
+ systemLock.unlock();
+ }
+
+ If this is called from two separate processes the resulting log file is
+ guaranteed to contain both lines.
+
+ When you call lock(), other threads or processes that try to call lock()
+ with the same key will block until the thread or process that got the lock
+ calls unlock().
+
+ A non-blocking alternative to lock() is tryLock().
+ */
+
+/*!
+ Constructs a new system lock with \a key. The lock is created in an
+ unlocked state.
+
+ \sa lock(), key().
+ */
+QSystemLock::QSystemLock(const QString &key)
+{
+ d = new QSystemLockPrivate;
+ setKey(key);
+}
+
+/*!
+ Destroys a system lock.
+
+ warning: This will not unlock the system lock if it has been locked.
+*/
+QSystemLock::~QSystemLock()
+{
+ d->cleanHandle();
+ delete d;
+}
+
+/*!
+ Sets a new key to this system lock.
+
+ \sa key()
+ */
+void QSystemLock::setKey(const QString &key)
+{
+ if (key == d->key)
+ return;
+ d->cleanHandle();
+ d->lockCount = 0;
+ d->key = key;
+ // cache the file name so it doesn't have to be generated all the time.
+ d->fileName = d->makeKeyFileName();
+ d->error = QSystemLock::NoError;
+ d->errorString = QString();
+ d->handle();
+}
+
+/*!
+ Returns the key assigned to this system lock
+
+ \sa setKey()
+ */
+QString QSystemLock::key() const
+{
+ return d->key;
+}
+
+/*!
+ Locks the system lock. Lock \a mode can either be ReadOnly or ReadWrite.
+ If a mode is ReadOnly, attempts by other processes to obtain
+ ReadOnly locks will succeed, and ReadWrite attempts will block until
+ all of the ReadOnly locks are unlocked. If locked as ReadWrite, all
+ other attempts to lock will block until the lock is unlocked. A given
+ QSystemLock can be locked multiple times without blocking, and will
+ only be unlocked after a corresponding number of unlock()
+ calls are made. Returns true on success; otherwise returns false.
+
+ \sa unlock(), tryLock()
+ */
+bool QSystemLock::lock(LockMode mode)
+{
+ if (d->lockCount > 0 && mode == ReadOnly && d->lockedMode == ReadWrite) {
+ qWarning() << "QSystemLock::lock readwrite lock on top of readonly lock.";
+ return false;
+ }
+ return d->modifySemaphore(QSystemLockPrivate::Lock, mode);
+}
+
+/*!
+ Unlocks the system lock.
+ Returns true on success; otherwise returns false.
+
+ \sa lock()
+ */
+bool QSystemLock::unlock()
+{
+ if (d->lockCount == 0) {
+ qWarning() << "QSystemLock::unlock: unlock with no lock.";
+ return false;
+ }
+ return d->modifySemaphore(QSystemLockPrivate::Unlock, d->lockedMode);
+}
+
+/*!
+ Returns the type of error that occurred last or NoError.
+
+ \sa errorString()
+ */
+QSystemLock::SystemLockError QSystemLock::error() const
+{
+ return d->error;
+}
+
+/*!
+ Returns the human-readable message appropriate to the current error
+ reported by error(). If no suitable string is available, an empty
+ string is returned.
+
+ \sa error()
+ */
+QString QSystemLock::errorString() const
+{
+ return d->errorString;
+}
+
diff --git a/tests/auto/corelib/kernel/qtipc/qsharedmemory/src/qsystemlock.h b/tests/auto/corelib/kernel/qtipc/qsharedmemory/src/qsystemlock.h
new file mode 100644
index 0000000000..f9db4f879c
--- /dev/null
+++ b/tests/auto/corelib/kernel/qtipc/qsharedmemory/src/qsystemlock.h
@@ -0,0 +1,135 @@
+/****************************************************************************
+**
+** 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 QSYSTEMLOCK_H
+#define QSYSTEMLOCK_H
+
+#include <QtCore/qstring.h>
+
+QT_BEGIN_HEADER
+
+#ifndef QT_NO_SYSTEMLOCK
+
+QT_FORWARD_DECLARE_CLASS(QSystemLockPrivate)
+
+class QSystemLock
+{
+
+public:
+ enum SystemLockError
+ {
+ NoError,
+ UnknownError
+ };
+
+ QSystemLock(const QString &key);
+ ~QSystemLock();
+
+ void setKey(const QString &key);
+ QString key() const;
+
+ enum LockMode
+ {
+ ReadOnly,
+ ReadWrite
+ };
+
+ bool lock(LockMode mode = ReadWrite);
+ bool unlock();
+
+ SystemLockError error() const;
+ QString errorString() const;
+
+private:
+ Q_DISABLE_COPY(QSystemLock)
+
+ QSystemLockPrivate *d;
+};
+
+class QSystemLocker
+{
+
+public:
+ inline QSystemLocker(QSystemLock *systemLock,
+ QSystemLock::LockMode mode = QSystemLock::ReadWrite) : q_lock(systemLock)
+ {
+ autoUnLocked = relock(mode);
+ }
+
+ inline ~QSystemLocker()
+ {
+ if (autoUnLocked)
+ unlock();
+ }
+
+ inline QSystemLock *systemLock() const
+ {
+ return q_lock;
+ }
+
+ inline bool relock(QSystemLock::LockMode mode = QSystemLock::ReadWrite)
+ {
+ return (q_lock && q_lock->lock(mode));
+ }
+
+ inline bool unlock()
+ {
+ if (q_lock && q_lock->unlock()) {
+ autoUnLocked = false;
+ return true;
+ }
+ return false;
+ }
+
+private:
+ Q_DISABLE_COPY(QSystemLocker)
+
+ bool autoUnLocked;
+ QSystemLock *q_lock;
+};
+
+#endif // QT_NO_SYSTEMLOCK
+
+QT_END_HEADER
+
+#endif // QSYSTEMLOCK_H
+
diff --git a/tests/auto/corelib/kernel/qtipc/qsharedmemory/src/qsystemlock_p.h b/tests/auto/corelib/kernel/qtipc/qsharedmemory/src/qsystemlock_p.h
new file mode 100644
index 0000000000..628b005afc
--- /dev/null
+++ b/tests/auto/corelib/kernel/qtipc/qsharedmemory/src/qsystemlock_p.h
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** 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 QSYSTEMLOCK_P_H
+#define QSYSTEMLOCK_P_H
+
+#ifndef QT_NO_SYSTEMLOCK
+
+#include "qsystemlock.h"
+#include "private/qsharedmemory_p.h"
+#ifndef Q_OS_WINCE
+#include <sys/types.h>
+#endif
+
+#define MAX_LOCKS 64
+
+class QSystemLockPrivate
+{
+
+public:
+ QSystemLockPrivate();
+
+ QString makeKeyFileName()
+ {
+ return QSharedMemoryPrivate::makePlatformSafeKey(key, QLatin1String("qipc_systemlock_"));
+ }
+
+ void setErrorString(const QString &function);
+
+#ifdef Q_OS_WIN
+ HANDLE handle();
+ bool lock(HANDLE, int count);
+ bool unlock(HANDLE, int count);
+#else
+ key_t handle();
+#endif
+ void cleanHandle();
+
+ enum Operation {
+ Lock,
+ Unlock
+ };
+ bool modifySemaphore(Operation op, QSystemLock::LockMode mode = QSystemLock::ReadOnly);
+
+ QString key;
+ QString fileName;
+#ifdef Q_OS_WIN
+ HANDLE semaphore;
+ HANDLE semaphoreLock;
+#else
+ int semaphore;
+#endif
+ int lockCount;
+ QSystemLock::LockMode lockedMode;
+
+ QSystemLock::SystemLockError error;
+ QString errorString;
+
+private:
+#ifndef Q_OS_WIN
+ key_t unix_key;
+ bool createdFile;
+ bool createdSemaphore;
+#endif
+};
+
+#endif // QT_NO_SYSTEMLOCK
+
+#endif // QSYSTEMLOCK_P_H
+
diff --git a/tests/auto/corelib/kernel/qtipc/qsharedmemory/src/qsystemlock_unix.cpp b/tests/auto/corelib/kernel/qtipc/qsharedmemory/src/qsystemlock_unix.cpp
new file mode 100644
index 0000000000..984379619e
--- /dev/null
+++ b/tests/auto/corelib/kernel/qtipc/qsharedmemory/src/qsystemlock_unix.cpp
@@ -0,0 +1,234 @@
+/****************************************************************************
+**
+** 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 "qsystemlock.h"
+#include "qsystemlock_p.h"
+
+#include <qdebug.h>
+#include <qfile.h>
+
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/shm.h>
+#include <unistd.h>
+
+#include <sys/sem.h>
+// We have to define this as on some sem.h will have it
+union qt_semun {
+ int val; /* value for SETVAL */
+ struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */
+ unsigned short *array; /* array for GETALL, SETALL */
+};
+
+#define tr(x) QT_TRANSLATE_NOOP(QLatin1String("QSystemLock"), (x))
+
+#if defined(Q_OS_SYMBIAN)
+int createUnixKeyFile(const QString &fileName)
+{
+ if (QFile::exists(fileName))
+ return 0;
+
+ int fd = open(QFile::encodeName(fileName).constData(),
+ O_EXCL | O_CREAT | O_RDWR, 0640);
+ if (-1 == fd) {
+ if (errno == EEXIST)
+ return 0;
+ return -1;
+ } else {
+ close(fd);
+ }
+ return 1;
+}
+#endif
+
+QSystemLockPrivate::QSystemLockPrivate() :
+ semaphore(-1), lockCount(0),
+ error(QSystemLock::NoError), unix_key(-1), createdFile(false), createdSemaphore(false)
+{
+}
+
+void QSystemLockPrivate::setErrorString(const QString &function)
+{
+ switch (errno) {
+ case EIDRM:
+ errorString = function + QLatin1String(": ") + tr("The semaphore set was removed");
+ error = QSystemLock::UnknownError;
+ break;
+ default:
+ errorString = function + QLatin1String(": ") + tr("unknown error");
+ error = QSystemLock::UnknownError;
+ qWarning() << errorString << "key" << key << "errno" << errno << ERANGE << ENOMEM << EINVAL << EINTR << EFBIG << EFAULT << EAGAIN << EACCES << E2BIG;
+ }
+}
+
+/*!
+ \internal
+
+ Setup unix_key
+ */
+key_t QSystemLockPrivate::handle()
+{
+ if (key.isEmpty())
+ return -1;
+
+ // ftok requires that an actual file exists somewhere
+ // If we have already made at some point in the past,
+ // double check that it is still there.
+ if (-1 != unix_key) {
+ int aNewunix_key = ftok(QFile::encodeName(fileName).constData(), 'Q');
+ if (aNewunix_key != unix_key) {
+ cleanHandle();
+ } else {
+ return unix_key;
+ }
+ }
+
+ // Create the file needed for ftok
+#if defined(Q_OS_SYMBIAN)
+ int built = createUnixKeyFile(fileName);
+#else
+ int built = QSharedMemoryPrivate::createUnixKeyFile(fileName);
+#endif
+ if (-1 == built)
+ return -1;
+ createdFile = (1 == built);
+
+ // Get the unix key for the created file
+ unix_key = ftok(QFile::encodeName(fileName).constData(), 'Q');
+ if (-1 == unix_key) {
+ setErrorString(QLatin1String("QSystemLock::handle ftok"));
+ return -1;
+ }
+
+ // Get semaphore
+ semaphore = semget(unix_key, 1, 0666 | IPC_CREAT | IPC_EXCL);
+ if (-1 == semaphore) {
+ if (errno == EEXIST)
+ semaphore = semget(unix_key, 1, 0666 | IPC_CREAT);
+ if (-1 == semaphore) {
+ setErrorString(QLatin1String("QSystemLock::handle semget"));
+ cleanHandle();
+ return -1;
+ }
+ } else {
+ // Created semaphore, initialize value.
+ createdSemaphore = true;
+ qt_semun init_op;
+ init_op.val = MAX_LOCKS;
+ if (-1 == semctl(semaphore, 0, SETVAL, init_op)) {
+ setErrorString(QLatin1String("QSystemLock::handle semctl"));
+ cleanHandle();
+ return -1;
+ }
+ }
+
+ return unix_key;
+}
+
+/*!
+ \internal
+
+ Cleanup the unix_key
+ */
+void QSystemLockPrivate::cleanHandle()
+{
+ unix_key = -1;
+
+ // remove the file if we made it
+ if (createdFile) {
+ if (!QFile::remove(fileName))
+ setErrorString(QLatin1String("QSystemLock::cleanHandle QFile::remove"));
+ createdFile = false;
+ }
+
+ if (createdSemaphore) {
+ if (-1 != semaphore) {
+ if (-1 == semctl(semaphore, 0, IPC_RMID)) {
+ setErrorString(QLatin1String("QSystemLock::cleanHandle semctl"));
+ }
+ semaphore = -1;
+ }
+ createdSemaphore = false;
+ }
+}
+
+/*!
+ \internal
+
+ modifySemaphore generates operation.sem_op and handles recursive behavior.
+ */
+bool QSystemLockPrivate::modifySemaphore(QSystemLockPrivate::Operation op,
+ QSystemLock::LockMode mode)
+{
+ if (-1 == handle())
+ return false;
+
+ if ((lockCount == 0 && op == Lock) || (lockCount > 0 && op == Unlock)) {
+ if (op == Unlock) {
+ --lockCount;
+ if (lockCount < 0)
+ qFatal("%s: lockCount must not be negative", Q_FUNC_INFO);
+ if (lockCount > 0)
+ return true;
+ }
+
+ struct sembuf operation;
+ operation.sem_num = 0;
+ operation.sem_op = (mode == QSystemLock::ReadWrite) ? MAX_LOCKS : 1;
+ if (op == Lock)
+ operation.sem_op *= -1;
+ operation.sem_flg = SEM_UNDO;
+
+ if (-1 == semop(semaphore, &operation, 1)) {
+ setErrorString(QLatin1String("QSystemLock::modify"));
+ return false;
+ }
+ lockedMode = mode;
+ }
+ if (op == Lock)
+ lockCount++;
+
+ return true;
+}
+
diff --git a/tests/auto/corelib/kernel/qtipc/qsharedmemory/src/qsystemlock_win.cpp b/tests/auto/corelib/kernel/qtipc/qsharedmemory/src/qsystemlock_win.cpp
new file mode 100644
index 0000000000..c04b596210
--- /dev/null
+++ b/tests/auto/corelib/kernel/qtipc/qsharedmemory/src/qsystemlock_win.cpp
@@ -0,0 +1,183 @@
+/****************************************************************************
+**
+** 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 "qsystemlock.h"
+#include "qsystemlock_p.h"
+#include <qdebug.h>
+#include <QtCore>
+QSystemLockPrivate::QSystemLockPrivate() :
+ semaphore(0), semaphoreLock(0),
+ lockCount(0), error(QSystemLock::NoError)
+{
+}
+
+void QSystemLockPrivate::setErrorString(const QString &function)
+{
+ BOOL windowsError = GetLastError();
+ if (windowsError == 0)
+ return;
+ errorString = function + QLatin1String(": ")
+ + QLatin1String("Unknown error");
+ error = QSystemLock::UnknownError;
+ qWarning() << errorString << "key" << key << (int)windowsError << semaphore << semaphoreLock;
+}
+
+/*!
+ \internal
+
+ Setup the semaphore
+ */
+HANDLE QSystemLockPrivate::handle()
+{
+ // don't allow making handles on empty keys
+ if (key.isEmpty())
+ return 0;
+
+ // Create it if it doesn't already exists.
+ if (semaphore == 0) {
+ QString safeName = makeKeyFileName();
+ semaphore = CreateSemaphore(0, MAX_LOCKS, MAX_LOCKS, (wchar_t*)safeName.utf16());
+
+ if (semaphore == 0) {
+ setErrorString(QLatin1String("QSystemLockPrivate::handle"));
+ return 0;
+ }
+ }
+
+ if (semaphoreLock == 0) {
+ QString safeLockName = QSharedMemoryPrivate::makePlatformSafeKey(key + QLatin1String("lock"), QLatin1String("qipc_systemlock_"));
+ semaphoreLock = CreateSemaphore(0, 1, 1, (wchar_t*)safeLockName.utf16());
+
+ if (semaphoreLock == 0) {
+ setErrorString(QLatin1String("QSystemLockPrivate::handle"));
+ return 0;
+ }
+ }
+
+ return semaphore;
+}
+
+/*!
+ \internal
+
+ Cleanup the semaphore
+ */
+void QSystemLockPrivate::cleanHandle()
+{
+ if (semaphore && !CloseHandle(semaphore))
+ setErrorString(QLatin1String("QSystemLockPrivate::cleanHandle:"));
+ if (semaphoreLock && !CloseHandle(semaphoreLock))
+ setErrorString(QLatin1String("QSystemLockPrivate::cleanHandle:"));
+ semaphore = 0;
+ semaphoreLock = 0;
+}
+
+bool QSystemLockPrivate::lock(HANDLE handle, int count)
+{
+ if (count == 1) {
+ WaitForSingleObject(handle, INFINITE);
+ return true;
+ }
+
+ int i = count;
+ while (i > 0) {
+ if (WAIT_OBJECT_0 == WaitForSingleObject(handle, 0)) {
+ --i;
+ } else {
+ // undo what we have done, sleep and then try again later
+ ReleaseSemaphore(handle, (count - i), 0);
+ i = count;
+ ReleaseSemaphore(semaphoreLock, 1, 0);
+ Sleep(1);
+ WaitForSingleObject(semaphoreLock, INFINITE);
+ }
+ }
+ return true;
+}
+
+bool QSystemLockPrivate::unlock(HANDLE handle, int count)
+{
+ if (0 == ReleaseSemaphore(handle, count, 0)) {
+ setErrorString(QLatin1String("QSystemLockPrivate::unlock"));
+ return false;
+ }
+ return true;
+}
+
+/*!
+ \internal
+
+ modifySemaphore handles recursive behavior and modifies the semaphore.
+ */
+bool QSystemLockPrivate::modifySemaphore(QSystemLockPrivate::Operation op,
+ QSystemLock::LockMode mode)
+{
+ if (0 == handle())
+ return false;
+
+ if ((lockCount == 0 && op == Lock) || (lockCount > 0 && op == Unlock)) {
+ if (op == Unlock) {
+ --lockCount;
+ if (lockCount < 0)
+ qFatal("%s: lockCount must not be negative", Q_FUNC_INFO);
+ if (lockCount > 0)
+ return true;
+ }
+
+ int count = (mode == QSystemLock::ReadWrite) ? MAX_LOCKS : 1;
+ if (op == Lock) {
+ lock(semaphoreLock, 1);
+ lock(semaphore, count);
+ if (count != MAX_LOCKS) unlock(semaphoreLock, 1);
+ lockedMode = mode;
+ } else {
+ if (count == MAX_LOCKS) unlock(semaphoreLock, 1);
+ unlock(semaphore, count);
+ }
+
+ }
+ if (op == Lock)
+ lockCount++;
+
+ return true;
+}
+
diff --git a/tests/auto/corelib/kernel/qtipc/qsharedmemory/src/src.pri b/tests/auto/corelib/kernel/qtipc/qsharedmemory/src/src.pri
new file mode 100644
index 0000000000..8aaf528447
--- /dev/null
+++ b/tests/auto/corelib/kernel/qtipc/qsharedmemory/src/src.pri
@@ -0,0 +1,11 @@
+INCLUDEPATH += $$PWD
+DEPENDPATH += $$PWD
+QT += core-private
+
+SOURCES += $$PWD/qsystemlock.cpp
+
+HEADERS += $$PWD/qsystemlock.h \
+ $$PWD/qsystemlock_p.h
+
+unix:SOURCES += $$PWD/qsystemlock_unix.cpp
+win32:SOURCES += $$PWD/qsystemlock_win.cpp
diff --git a/tests/auto/corelib/kernel/qtipc/qsharedmemory/test/test.pro b/tests/auto/corelib/kernel/qtipc/qsharedmemory/test/test.pro
new file mode 100644
index 0000000000..50c266986c
--- /dev/null
+++ b/tests/auto/corelib/kernel/qtipc/qsharedmemory/test/test.pro
@@ -0,0 +1,37 @@
+load(qttest_p4)
+
+include(../src/src.pri)
+QT -= gui
+
+DEFINES += QSHAREDMEMORY_DEBUG
+DEFINES += QSYSTEMSEMAPHORE_DEBUG
+
+SOURCES += ../tst_qsharedmemory.cpp
+TARGET = ../tst_qsharedmemory
+
+!wince*:win32 {
+ CONFIG(debug, debug|release) {
+ TARGET = ../../debug/tst_qsharedmemory
+} else {
+ TARGET = ../../release/tst_qsharedmemory
+ }
+}
+
+wince*:{
+requires(contains(QT_CONFIG,script))
+QT += gui script
+addFiles.files = $$OUT_PWD/../../lackey/lackey.exe ../../lackey/scripts
+addFiles.path = .
+DEPLOYMENT += addFiles
+DEFINES += SRCDIR=\\\".\\\"
+}else:symbian{
+requires(contains(QT_CONFIG,script))
+QT += gui script
+addFiles.files = ../../lackey/scripts
+addFiles.path = /data/qsharedmemorytemp/lackey
+addBin.files = lackey.exe
+addBin.path = /sys/bin
+DEPLOYMENT += addFiles addBin
+} else {
+DEFINES += SRCDIR=\\\"$$PWD/../\\\"
+}
diff --git a/tests/auto/corelib/kernel/qtipc/qsharedmemory/tst_qsharedmemory.cpp b/tests/auto/corelib/kernel/qtipc/qsharedmemory/tst_qsharedmemory.cpp
new file mode 100644
index 0000000000..38c6f0e8ee
--- /dev/null
+++ b/tests/auto/corelib/kernel/qtipc/qsharedmemory/tst_qsharedmemory.cpp
@@ -0,0 +1,836 @@
+/****************************************************************************
+**
+** 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 <qsharedmemory.h>
+#include <QtCore/QFile>
+
+//TESTED_CLASS=
+//TESTED_FILES=
+
+#define EXISTING_SHARE "existing"
+#define EXISTING_SIZE 1024
+
+#ifdef Q_OS_SYMBIAN
+#define SRCDIR "c:/data/qsharedmemorytemp/lackey/"
+#define LACKEYDIR SRCDIR "lackey"
+#elif defined(Q_OS_WINCE)
+#define LACKEYDIR SRCDIR
+#else
+#define LACKEYDIR "../lackey"
+#endif
+
+Q_DECLARE_METATYPE(QSharedMemory::SharedMemoryError)
+Q_DECLARE_METATYPE(QSharedMemory::AccessMode)
+
+class tst_QSharedMemory : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QSharedMemory();
+ virtual ~tst_QSharedMemory();
+
+public Q_SLOTS:
+ void init();
+ void cleanup();
+
+private slots:
+ // basics
+ void constructor();
+ void key_data();
+ void key();
+ void create_data();
+ void create();
+ void attach_data();
+ void attach();
+ void lock();
+
+ // custom edge cases
+ void removeWhileAttached();
+ void emptyMemory();
+ void readOnly();
+
+ // basics all together
+ void simpleProducerConsumer_data();
+ void simpleProducerConsumer();
+ void simpleDoubleProducerConsumer();
+
+ // with threads
+ void simpleThreadedProducerConsumer_data();
+ void simpleThreadedProducerConsumer();
+
+ // with processes
+ void simpleProcessProducerConsumer_data();
+ void simpleProcessProducerConsumer();
+
+ // extreme cases
+ void useTooMuchMemory();
+ void attachTooMuch();
+
+ // unique keys
+ void uniqueKey_data();
+ void uniqueKey();
+
+protected:
+ int remove(const QString &key);
+
+ QString rememberKey(const QString &key)
+ {
+ if (key == EXISTING_SHARE)
+ return key;
+ if (!keys.contains(key)) {
+ keys.append(key);
+ remove(key);
+ }
+ return key;
+ }
+
+ QStringList keys;
+ QList<QSharedMemory*> jail;
+ QSharedMemory *existingSharedMemory;
+};
+
+tst_QSharedMemory::tst_QSharedMemory() : existingSharedMemory(0)
+{
+}
+
+tst_QSharedMemory::~tst_QSharedMemory()
+{
+}
+
+void tst_QSharedMemory::init()
+{
+ existingSharedMemory = new QSharedMemory(EXISTING_SHARE);
+ if (!existingSharedMemory->create(EXISTING_SIZE)) {
+ QVERIFY(existingSharedMemory->error() == QSharedMemory::AlreadyExists);
+ }
+}
+
+void tst_QSharedMemory::cleanup()
+{
+ delete existingSharedMemory;
+ qDeleteAll(jail.begin(), jail.end());
+ jail.clear();
+
+ keys.append(EXISTING_SHARE);
+ for (int i = 0; i < keys.count(); ++i) {
+ QSharedMemory sm(keys.at(i));
+ if (!sm.create(1024)) {
+ //if(sm.error() != QSharedMemory::KeyError)
+ // qWarning() << "test cleanup: remove failed:" << keys.at(i) << sm.error() << sm.errorString();
+ sm.attach();
+ sm.detach();
+ remove(keys.at(i));
+ }
+ }
+}
+
+#ifndef Q_OS_WIN
+#include "private/qsharedmemory_p.h"
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#endif
+
+int tst_QSharedMemory::remove(const QString &key)
+{
+#ifndef Q_OS_WIN
+ // On unix the shared memory might exists from a previously failed test
+ // or segfault, remove it it does
+ if (key.isEmpty())
+ return -1;
+
+ // ftok requires that an actual file exists somewhere
+ QString fileName = QSharedMemoryPrivate::makePlatformSafeKey(key);
+ if (!QFile::exists(fileName)) {
+ //qDebug() << "exits failed";
+ return -2;
+ }
+
+ int unix_key = ftok(fileName.toLatin1().constData(), 'Q');
+ if (-1 == unix_key) {
+ qDebug() << "ftok failed";
+ return -3;
+ }
+
+ int id = shmget(unix_key, 0, 0660);
+ if (-1 == id) {
+ qDebug() << "shmget failed";
+ return -4;
+ }
+
+ struct shmid_ds shmid_ds;
+ if (-1 == shmctl(id, IPC_RMID, &shmid_ds)) {
+ qDebug() << "shmctl failed";
+ return -5;
+ }
+ return QFile::remove(fileName);
+#else
+ Q_UNUSED(key);
+ return 0;
+#endif
+}
+
+/*!
+ Tests the default values
+ */
+void tst_QSharedMemory::constructor()
+{
+ QSharedMemory sm;
+ QCOMPARE(sm.key(), QString());
+ QVERIFY(!sm.isAttached());
+ QVERIFY(sm.data() == 0);
+ QCOMPARE(sm.size(), 0);
+ QCOMPARE(sm.error(), QSharedMemory::NoError);
+ QVERIFY(sm.errorString() == QString());
+}
+
+void tst_QSharedMemory::key_data()
+{
+ QTest::addColumn<QString>("constructorKey");
+ QTest::addColumn<QString>("setKey");
+ QTest::addColumn<QString>("setNativeKey");
+
+ QTest::newRow("null, null, null") << QString() << QString() << QString();
+ QTest::newRow("one, null, null") << QString("one") << QString() << QString();
+ QTest::newRow("null, one, null") << QString() << QString("one") << QString();
+ QTest::newRow("null, null, one") << QString() << QString() << QString("one");
+ QTest::newRow("one, two, null") << QString("one") << QString("two") << QString();
+ QTest::newRow("one, null, two") << QString("one") << QString() << QString("two");
+ QTest::newRow("null, one, two") << QString() << QString("one") << QString("two");
+ QTest::newRow("one, two, three") << QString("one") << QString("two") << QString("three");
+ QTest::newRow("invalid") << QString("o/e") << QString("t/o") << QString("|x");
+}
+
+/*!
+ Basic key testing
+ */
+void tst_QSharedMemory::key()
+{
+ QFETCH(QString, constructorKey);
+ QFETCH(QString, setKey);
+ QFETCH(QString, setNativeKey);
+
+ QSharedMemory sm(constructorKey);
+ QCOMPARE(sm.key(), constructorKey);
+ QCOMPARE(sm.nativeKey().isEmpty(), constructorKey.isEmpty());
+ sm.setKey(setKey);
+ QCOMPARE(sm.key(), setKey);
+ QCOMPARE(sm.nativeKey().isEmpty(), setKey.isEmpty());
+ sm.setNativeKey(setNativeKey);
+ QVERIFY(sm.key().isNull());
+ QCOMPARE(sm.nativeKey(), setNativeKey);
+ QCOMPARE(sm.isAttached(), false);
+
+ QCOMPARE(sm.error(), QSharedMemory::NoError);
+ QVERIFY(sm.errorString() == QString());
+ QVERIFY(sm.data() == 0);
+ QCOMPARE(sm.size(), 0);
+
+ QCOMPARE(sm.detach(), false);
+}
+
+void tst_QSharedMemory::create_data()
+{
+ QTest::addColumn<QString>("key");
+ QTest::addColumn<int>("size");
+ QTest::addColumn<bool>("canCreate");
+ QTest::addColumn<QSharedMemory::SharedMemoryError>("error");
+
+ QTest::newRow("null key") << QString() << 1024
+ << false << QSharedMemory::KeyError;
+ QTest::newRow("-1 size") << QString("negsize") << -1
+ << false << QSharedMemory::InvalidSize;
+ QTest::newRow("nor size") << QString("norsize") << 1024
+ << true << QSharedMemory::NoError;
+ QTest::newRow("already exists") << QString(EXISTING_SHARE) << EXISTING_SIZE
+ << false << QSharedMemory::AlreadyExists;
+}
+
+/*!
+ Basic create testing
+ */
+void tst_QSharedMemory::create()
+{
+ QFETCH(QString, key);
+ QFETCH(int, size);
+ QFETCH(bool, canCreate);
+ QFETCH(QSharedMemory::SharedMemoryError, error);
+
+ QSharedMemory sm(rememberKey(key));
+ QCOMPARE(sm.create(size), canCreate);
+ if(sm.error() != error)
+ qDebug() << sm.errorString();
+ QCOMPARE(sm.key(), key);
+ if (canCreate) {
+ QVERIFY(sm.errorString() == QString());
+ QVERIFY(sm.data() != 0);
+ QVERIFY(sm.size() != 0);
+ } else {
+ QVERIFY(sm.data() == 0);
+ QVERIFY(sm.errorString() != QString());
+ }
+}
+
+void tst_QSharedMemory::attach_data()
+{
+ QTest::addColumn<QString>("key");
+ QTest::addColumn<bool>("exists");
+ QTest::addColumn<QSharedMemory::SharedMemoryError>("error");
+
+ QTest::newRow("null key") << QString() << false << QSharedMemory::KeyError;
+ QTest::newRow("doesn't exists") << QString("doesntexists") << false << QSharedMemory::NotFound;
+ QTest::newRow("already exists") << QString(EXISTING_SHARE) << true << QSharedMemory::NoError;
+}
+
+/*!
+ Basic attach/detach testing
+ */
+void tst_QSharedMemory::attach()
+{
+ QFETCH(QString, key);
+ QFETCH(bool, exists);
+ QFETCH(QSharedMemory::SharedMemoryError, error);
+#ifdef Q_OS_HPUX
+ if (QLatin1String(QTest::currentDataTag()) == QLatin1String("already exists")) {
+ QSKIP("HPUX doesn't allow for multiple attaches per process", SkipSingle);
+ }
+#endif
+ QSharedMemory sm(key);
+ QCOMPARE(sm.attach(), exists);
+ QCOMPARE(sm.isAttached(), exists);
+ QCOMPARE(sm.error(), error);
+ QCOMPARE(sm.key(), key);
+ if (exists) {
+ QVERIFY(sm.data() != 0);
+ QVERIFY(sm.size() != 0);
+ QCOMPARE(sm.errorString(), QString());
+ QVERIFY(sm.detach());
+ // Make sure detach doesn't screw up something and we can't re-attach.
+ QVERIFY(sm.attach());
+ QVERIFY(sm.data() != 0);
+ QVERIFY(sm.size() != 0);
+ QVERIFY(sm.detach());
+ QCOMPARE(sm.size(), 0);
+ QVERIFY(sm.data() == 0);
+ } else {
+ QVERIFY(sm.data() == 0);
+ QVERIFY(sm.size() == 0);
+ QVERIFY(sm.errorString() != QString());
+ QVERIFY(!sm.detach());
+ }
+}
+
+void tst_QSharedMemory::lock()
+{
+ QSharedMemory shm;
+ QVERIFY(!shm.lock());
+ QCOMPARE(shm.error(), QSharedMemory::LockError);
+
+ shm.setKey(QLatin1String("qsharedmemory"));
+
+ QVERIFY(!shm.lock());
+ QCOMPARE(shm.error(), QSharedMemory::LockError);
+
+ QVERIFY(shm.create(100));
+ QVERIFY(shm.lock());
+ QTest::ignoreMessage(QtWarningMsg, "QSharedMemory::lock: already locked");
+ QVERIFY(shm.lock());
+ // we didn't unlock(), so ignore the warning from auto-detach in destructor
+ QTest::ignoreMessage(QtWarningMsg, "QSharedMemory::lock: already locked");
+}
+
+/*!
+ Other shared memory are allowed to be attached after we remove,
+ but new shared memory are not allowed to attach after a remove.
+ */
+void tst_QSharedMemory::removeWhileAttached()
+{
+#ifdef Q_OS_HPUX
+ QSKIP("HPUX doesn't allow for multiple attaches per process", SkipAll);
+#endif
+ rememberKey("one");
+
+ // attach 1
+ QSharedMemory *smOne = new QSharedMemory(QLatin1String("one"));
+ QVERIFY(smOne->create(1024));
+ QVERIFY(smOne->isAttached());
+
+ // attach 2
+ QSharedMemory *smTwo = new QSharedMemory(QLatin1String("one"));
+ QVERIFY(smTwo->attach());
+ QVERIFY(smTwo->isAttached());
+
+ // detach 1 and remove, remove one first to catch another error.
+ delete smOne;
+ delete smTwo;
+
+ // three shouldn't be able to attach
+ QSharedMemory smThree(QLatin1String("one"));
+ QVERIFY(!smThree.attach());
+ QCOMPARE(smThree.error(), QSharedMemory::NotFound);
+}
+
+/*!
+ The memory should be set to 0 after created.
+ */
+void tst_QSharedMemory::emptyMemory()
+{
+ QSharedMemory sm(rememberKey(QLatin1String("voidland")));
+ int size = 1024;
+ QVERIFY(sm.create(size, QSharedMemory::ReadOnly));
+ char *get = (char*)sm.data();
+ char null = 0;
+ for (int i = 0; i < size; ++i)
+ QCOMPARE(get[i], null);
+}
+
+/*!
+ Verify that attach with ReadOnly is actually read only
+ by writing to data and causing a segfault.
+*/
+void tst_QSharedMemory::readOnly()
+{
+#ifdef Q_OS_WIN
+ QSKIP("This test opens a crash dialog on Windows", SkipSingle);
+#endif
+#if defined (Q_OS_SYMBIAN)
+ QSKIP("Readonly shared memory is not supported in symbian", SkipAll);
+#endif
+ QString program = LACKEYDIR "/lackey";
+ QStringList arguments;
+ rememberKey("readonly_segfault");
+ arguments << SRCDIR "../lackey/scripts/readonly_segfault.js";
+
+ // ### on windows disable the popup somehow
+ QProcess p;
+ p.start(program, arguments);
+ p.setProcessChannelMode(QProcess::ForwardedChannels);
+ p.waitForFinished();
+ QCOMPARE(p.error(), QProcess::Crashed);
+}
+
+/*!
+ Keep making shared memory until the kernel stops us.
+ */
+void tst_QSharedMemory::useTooMuchMemory()
+{
+#ifdef Q_OS_LINUX
+ bool success = true;
+ int count = 0;
+ while (success) {
+ QString key = QString("maxmemorytest_%1").arg(count++);
+ QSharedMemory *sm = new QSharedMemory(rememberKey(key));
+ QVERIFY(sm);
+ jail.append(sm);
+ int size = 32768 * 1024;
+ success = sm->create(size);
+ if (!success && sm->error() == QSharedMemory::AlreadyExists) {
+ // left over from a crash, clean it up
+ sm->attach();
+ sm->detach();
+ success = sm->create(size);
+ }
+
+ if (!success) {
+ QVERIFY(!sm->isAttached());
+ QCOMPARE(sm->key(), key);
+ QCOMPARE(sm->size(), 0);
+ QVERIFY(sm->data() == 0);
+ if (sm->error() != QSharedMemory::OutOfResources)
+ qDebug() << sm->error() << sm->errorString();
+ // ### Linux wont return OutOfResources if there are not enough semaphores to use.
+ QVERIFY(sm->error() == QSharedMemory::OutOfResources
+ || sm->error() == QSharedMemory::LockError);
+ QVERIFY(sm->errorString() != QString());
+ QVERIFY(!sm->attach());
+ QVERIFY(!sm->detach());
+ } else {
+ QVERIFY(sm->isAttached());
+ }
+ }
+#endif
+}
+
+/*!
+ Create one shared memory (government) and see how many other shared memories (wars) we can
+ attach before the system runs out of resources.
+ */
+void tst_QSharedMemory::attachTooMuch()
+{
+ QSKIP("disabled", SkipAll);
+#ifdef Q_OS_HPUX
+ QSKIP("HPUX doesn't allow for multiple attaches per process", SkipAll);
+#endif
+#ifdef Q_OS_WINCE
+ QSKIP("This nearly kills the system itself, so skip for Qt/WinCE", SkipAll);
+#endif
+ QSharedMemory government(rememberKey("government"));
+ QVERIFY(government.create(1024));
+ while (true) {
+ QSharedMemory *war = new QSharedMemory(government.key());
+ QVERIFY(war);
+ jail.append(war);
+ if (!war->attach()) {
+ QVERIFY(!war->isAttached());
+ QCOMPARE(war->key(), government.key());
+ QCOMPARE(war->size(), 0);
+ QVERIFY(war->data() == 0);
+ QCOMPARE(war->error(), QSharedMemory::OutOfResources);
+ QVERIFY(war->errorString() != QString());
+ QVERIFY(!war->detach());
+ break;
+ } else {
+ QVERIFY(war->isAttached());
+ }
+ }
+}
+
+void tst_QSharedMemory::simpleProducerConsumer_data()
+{
+ QTest::addColumn<QSharedMemory::AccessMode>("mode");
+
+ QTest::newRow("readonly") << QSharedMemory::ReadOnly;
+ QTest::newRow("readwrite") << QSharedMemory::ReadWrite;
+}
+
+/*!
+ The basic consumer producer that rounds out the basic testing.
+ If this fails then any muli-threading/process might fail (but be
+ harder to debug)
+
+ This doesn't require nor test any locking system.
+ */
+void tst_QSharedMemory::simpleProducerConsumer()
+{
+#ifdef Q_OS_HPUX
+ QSKIP("HPUX doesn't allow for multiple attaches per process", SkipAll);
+#endif
+ QFETCH(QSharedMemory::AccessMode, mode);
+
+ rememberKey(QLatin1String("market"));
+ QSharedMemory producer(QLatin1String("market"));
+ QSharedMemory consumer(QLatin1String("market"));
+ int size = 512;
+ QVERIFY(producer.create(size));
+ QVERIFY(consumer.attach(mode));
+
+ char *put = (char*)producer.data();
+ char *get = (char*)consumer.data();
+ // On Windows CE you always have ReadWrite access. Thus
+ // ViewMapOfFile returns the same pointer
+ // On Symbian, the address will always be same, as
+ // write protection of chunks is not currently supported by Symbian
+#if !defined(Q_OS_WINCE) && !defined(Q_OS_SYMBIAN)
+ QVERIFY(put != get);
+#endif
+ for (int i = 0; i < size; ++i) {
+ put[i] = 'Q';
+ QCOMPARE(get[i], 'Q');
+ }
+ QVERIFY(consumer.detach());
+}
+
+void tst_QSharedMemory::simpleDoubleProducerConsumer()
+{
+#ifdef Q_OS_HPUX
+ QSKIP("HPUX doesn't allow for multiple attaches per process", SkipAll);
+#endif
+ rememberKey(QLatin1String("market"));
+ QSharedMemory producer(QLatin1String("market"));
+ int size = 512;
+ QVERIFY(producer.create(size));
+ QVERIFY(producer.detach());
+ QVERIFY(producer.create(size));
+
+ {
+ QSharedMemory consumer(QLatin1String("market"));
+ QVERIFY(consumer.attach());
+ }
+}
+
+class Consumer : public QThread
+{
+
+public:
+ void run()
+ {
+ QSharedMemory consumer(QLatin1String("market"));
+ while (!consumer.attach()) {
+ if (consumer.error() != QSharedMemory::NotFound)
+ qDebug() << "consumer: failed to connect" << consumer.error() << consumer.errorString();
+ QVERIFY(consumer.error() == QSharedMemory::NotFound || consumer.error() == QSharedMemory::KeyError);
+ QTest::qWait(1);
+ }
+
+ char *memory = (char*)consumer.data();
+
+ int i = 0;
+ while (true) {
+ if(!consumer.lock())
+ break;
+ if (memory[0] == 'Q')
+ memory[0] = ++i;
+ if (memory[0] == 'E') {
+ memory[1]++;
+ QVERIFY(consumer.unlock());
+ break;
+ }
+ QVERIFY(consumer.unlock());
+ QTest::qWait(1);
+ }
+
+ QVERIFY(consumer.detach());
+ }
+};
+
+class Producer : public QThread
+{
+
+public:
+ void run()
+ {
+ QSharedMemory producer(QLatin1String("market"));
+ int size = 1024;
+ if (!producer.create(size)) {
+ // left over from a crash...
+ if (producer.error() == QSharedMemory::AlreadyExists) {
+ producer.attach();
+ producer.detach();
+ QVERIFY(producer.create(size));
+ }
+ }
+ QVERIFY(producer.isAttached());
+ char *memory = (char*)producer.data();
+ memory[1] = '0';
+#if defined(Q_OS_SYMBIAN)
+ // Sleep a while to ensure that consumers start properly
+ QTest::qSleep(1000);
+#endif
+ QTime timer;
+ timer.start();
+ int i = 0;
+ while (i < 5 && timer.elapsed() < 5000) {
+ QVERIFY(producer.lock());
+ if (memory[0] == 'Q') {
+ QVERIFY(producer.unlock());
+ QTest::qWait(1);
+ continue;
+ }
+ ++i;
+ memory[0] = 'Q';
+ QVERIFY(producer.unlock());
+ QTest::qWait(1);
+ }
+
+ // tell everyone to quit
+ QVERIFY(producer.lock());
+ memory[0] = 'E';
+ QVERIFY(producer.unlock());
+
+ }
+private:
+
+};
+
+void tst_QSharedMemory::simpleThreadedProducerConsumer_data()
+{
+ QTest::addColumn<bool>("producerIsThread");
+ QTest::addColumn<int>("threads");
+ for (int i = 0; i < 5; ++i) {
+ QTest::newRow("1 consumer, producer is thread") << true << 1;
+ QTest::newRow("1 consumer, producer is this") << false << 1;
+ QTest::newRow("5 consumers, producer is thread") << true << 5;
+ QTest::newRow("5 consumers, producer is this") << false << 5;
+ }
+}
+
+/*!
+ The basic producer/consumer, but this time using threads.
+ */
+void tst_QSharedMemory::simpleThreadedProducerConsumer()
+{
+ QFETCH(bool, producerIsThread);
+ QFETCH(int, threads);
+ rememberKey(QLatin1String("market"));
+
+#if defined Q_OS_HPUX && defined __ia64
+ QSKIP("This test locks up on gravlaks.troll.no", SkipSingle);
+#endif
+
+ Producer p;
+#if defined(Q_OS_SYMBIAN)
+ enum
+ {
+ /**
+ * The maximum stack size.
+ */
+ SymbianStackSize = 0x14000
+ };
+ p.setStackSize(SymbianStackSize);
+#endif
+ if (producerIsThread)
+ p.start();
+
+ QList<Consumer*> consumers;
+ for (int i = 0; i < threads; ++i) {
+ consumers.append(new Consumer());
+#if defined(Q_OS_SYMBIAN)
+ consumers.last()->setStackSize(SymbianStackSize);
+#endif
+ consumers.last()->start();
+ }
+
+ if (!producerIsThread)
+ p.run();
+
+ p.wait(5000);
+ while (!consumers.isEmpty()) {
+ QVERIFY(consumers.first()->wait(5000));
+ delete consumers.takeFirst();
+ }
+}
+
+void tst_QSharedMemory::simpleProcessProducerConsumer_data()
+{
+ QTest::addColumn<int>("processes");
+ int tries = 5;
+ for (int i = 0; i < tries; ++i) {
+ QTest::newRow("1 process") << 1;
+ QTest::newRow("5 processes") << 5;
+ }
+}
+
+/*!
+ Create external processes that produce and consume.
+ */
+void tst_QSharedMemory::simpleProcessProducerConsumer()
+{
+#if defined (Q_OS_SYMBIAN) && defined(Q_CC_NOKIAX86)
+ QSKIP("Cannot launch multiple Qt processes in Symbian emulator", SkipAll);
+#endif
+ QFETCH(int, processes);
+
+ rememberKey("market");
+
+ QStringList arguments = QStringList() << SRCDIR "../lackey/scripts/producer.js";
+ QProcess producer;
+ producer.setProcessChannelMode(QProcess::ForwardedChannels);
+ producer.start( LACKEYDIR "/lackey", arguments);
+ producer.waitForStarted();
+ QVERIFY(producer.error() != QProcess::FailedToStart);
+
+ QList<QProcess*> consumers;
+ unsigned int failedProcesses = 0;
+ for (int i = 0; i < processes; ++i) {
+ QStringList arguments = QStringList() << SRCDIR "../lackey/scripts/consumer.js";
+ QProcess *p = new QProcess;
+ p->setProcessChannelMode(QProcess::ForwardedChannels);
+#ifdef Q_OS_WINCE
+ // We can't start the same executable twice on Windows CE.
+ // Create a copy instead.
+ QString lackeyCopy = QLatin1String(LACKEYDIR "/lackey");
+ lackeyCopy.append(QString::number(i));
+ lackeyCopy.append(QLatin1String(".exe"));
+ if (!QFile::exists(lackeyCopy))
+ QVERIFY(QFile::copy(LACKEYDIR "/lackey.exe", lackeyCopy));
+ p->start(lackeyCopy, arguments);
+#else
+ p->start(LACKEYDIR "/lackey", arguments);
+#endif
+
+ if (p->waitForStarted(2000))
+ consumers.append(p);
+ else
+ ++failedProcesses;
+ }
+
+ QVERIFY(producer.waitForFinished(5000));
+
+ bool consumerFailed = false;
+
+ while (!consumers.isEmpty()) {
+ QVERIFY(consumers.first()->waitForFinished(3000));
+ if (consumers.first()->state() == QProcess::Running ||
+ consumers.first()->exitStatus() != QProcess::NormalExit ||
+ consumers.first()->exitCode() != 0) {
+ consumerFailed = true;
+ }
+ delete consumers.takeFirst();
+ }
+ QCOMPARE(consumerFailed, false);
+ QCOMPARE(failedProcesses, (unsigned int)(0));
+}
+
+void tst_QSharedMemory::uniqueKey_data()
+{
+ QTest::addColumn<QString>("key1");
+ QTest::addColumn<QString>("key2");
+
+ QTest::newRow("null == null") << QString() << QString();
+ QTest::newRow("key == key") << QString("key") << QString("key");
+ QTest::newRow("key1 == key1") << QString("key1") << QString("key1");
+ QTest::newRow("key != key1") << QString("key") << QString("key1");
+ QTest::newRow("ke1y != key1") << QString("ke1y") << QString("key1");
+ QTest::newRow("key1 != key2") << QString("key1") << QString("key2");
+}
+
+void tst_QSharedMemory::uniqueKey()
+{
+ QFETCH(QString, key1);
+ QFETCH(QString, key2);
+
+ QSharedMemory sm1(key1);
+ QSharedMemory sm2(key2);
+
+ bool setEqual = (key1 == key2);
+ bool keyEqual = (sm1.key() == sm2.key());
+ bool nativeEqual = (sm1.nativeKey() == sm2.nativeKey());
+
+ QCOMPARE(keyEqual, setEqual);
+ QCOMPARE(nativeEqual, setEqual);
+}
+
+QTEST_MAIN(tst_QSharedMemory)
+#include "tst_qsharedmemory.moc"
+
diff --git a/tests/auto/corelib/kernel/qtipc/qsystemsemaphore/.gitignore b/tests/auto/corelib/kernel/qtipc/qsystemsemaphore/.gitignore
new file mode 100644
index 0000000000..2b60bfb3e6
--- /dev/null
+++ b/tests/auto/corelib/kernel/qtipc/qsystemsemaphore/.gitignore
@@ -0,0 +1 @@
+tst_qsystemsemaphore
diff --git a/tests/auto/corelib/kernel/qtipc/qsystemsemaphore/files.qrc b/tests/auto/corelib/kernel/qtipc/qsystemsemaphore/files.qrc
new file mode 100644
index 0000000000..62966903e8
--- /dev/null
+++ b/tests/auto/corelib/kernel/qtipc/qsystemsemaphore/files.qrc
@@ -0,0 +1,7 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource>
+ <file alias="systemsemaphore_acquire.js">../lackey/scripts/systemsemaphore_acquire.js</file>
+ <file alias="systemsemaphore_release.js">../lackey/scripts/systemsemaphore_release.js</file>
+ <file alias="systemsemaphore_acquirerelease.js">../lackey/scripts/systemsemaphore_acquirerelease.js</file>
+</qresource>
+</RCC>
diff --git a/tests/auto/corelib/kernel/qtipc/qsystemsemaphore/qsystemsemaphore.pro b/tests/auto/corelib/kernel/qtipc/qsystemsemaphore/qsystemsemaphore.pro
new file mode 100644
index 0000000000..e05b6b878b
--- /dev/null
+++ b/tests/auto/corelib/kernel/qtipc/qsystemsemaphore/qsystemsemaphore.pro
@@ -0,0 +1,38 @@
+load(qttest_p4)
+#QT = core
+
+include(../qsharedmemory/src/src.pri)
+win32: CONFIG += console
+
+DEFINES += QSHAREDMEMORY_DEBUG
+DEFINES += QSYSTEMSEMAPHORE_DEBUG
+
+SOURCES += tst_qsystemsemaphore.cpp
+TARGET = tst_qsystemsemaphore
+
+RESOURCES += files.qrc
+
+wince*: {
+requires(contains(QT_CONFIG,script))
+# this test calls lackey, which then again depends on QtScript.
+# let's add it here so that it gets deployed easily
+QT += script
+lackey.files = $$OUT_PWD/../lackey/lackey.exe ../lackey/scripts
+lackey.path = .
+DEPLOYMENT += lackey
+}
+
+symbian: {
+requires(contains(QT_CONFIG,script))
+# this test calls lackey, which then again depends on QtScript.
+# let's add it here so that it gets deployed easily
+QT += script
+
+lackey.files = ../lackey/lackey.exe
+lackey.path = /sys/bin
+DEPLOYMENT += lackey
+
+# PowerMgmt capability needed to kill lackey process
+TARGET.CAPABILITY = PowerMgmt
+}
+
diff --git a/tests/auto/corelib/kernel/qtipc/qsystemsemaphore/tst_qsystemsemaphore.cpp b/tests/auto/corelib/kernel/qtipc/qsystemsemaphore/tst_qsystemsemaphore.cpp
new file mode 100644
index 0000000000..df625b8661
--- /dev/null
+++ b/tests/auto/corelib/kernel/qtipc/qsystemsemaphore/tst_qsystemsemaphore.cpp
@@ -0,0 +1,319 @@
+/****************************************************************************
+**
+** 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 <qsystemsemaphore.h>
+//TESTED_CLASS=
+//TESTED_FILES=
+
+#define EXISTING_SHARE "existing"
+#ifdef Q_OS_WINCE
+#define LACKEYLOC "."
+#else
+#define LACKEYLOC "../lackey"
+#endif
+#define LACKYWAITTIME 10000
+
+class tst_QSystemSemaphore : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QSystemSemaphore();
+ virtual ~tst_QSystemSemaphore();
+
+public Q_SLOTS:
+ void init();
+ void cleanup();
+
+private slots:
+ void key_data();
+ void key();
+
+ void basicacquire();
+ void complexacquire();
+
+ void basicProcesses();
+
+ void processes_data();
+ void processes();
+
+ void undo();
+ void initialValue();
+
+private:
+ QSystemSemaphore *existingLock;
+
+ QString makeFile(const QString &resource)
+ {
+ QFile memory(resource);
+ if (!memory.open(QIODevice::ReadOnly)) {
+ qDebug() << "error reading resource" << resource;
+ return QString();
+ }
+ QTemporaryFile *file = new QTemporaryFile;
+ file->open();
+ file->write(memory.readAll());
+ tempFiles.append(file);
+ file->flush();
+#ifdef Q_OS_WINCE
+ // flush does not flush to disk on Windows CE. It flushes it into its application
+ // cache. Thus we need to close the file to be able that other processes(lackey) can read it
+ QString fileName = file->fileName();
+ file->close();
+ return fileName;
+#endif
+ return file->fileName();
+ }
+
+ QString acquire_js() { return makeFile(":/systemsemaphore_acquire.js"); }
+ QString release_js() { return makeFile(":/systemsemaphore_release.js"); }
+ QString acquirerelease_js() { return makeFile(":/systemsemaphore_acquirerelease.js"); }
+ QList<QTemporaryFile*> tempFiles;
+};
+
+tst_QSystemSemaphore::tst_QSystemSemaphore()
+{
+ if (!QFile::exists(LACKEYLOC "/lackey"))
+ qWarning() << "lackey executable doesn't exists!";
+}
+
+tst_QSystemSemaphore::~tst_QSystemSemaphore()
+{
+ qDeleteAll(tempFiles);
+}
+
+void tst_QSystemSemaphore::init()
+{
+ existingLock = new QSystemSemaphore(EXISTING_SHARE, 1, QSystemSemaphore::Create);
+}
+
+void tst_QSystemSemaphore::cleanup()
+{
+ delete existingLock;
+}
+
+void tst_QSystemSemaphore::key_data()
+{
+ QTest::addColumn<QString>("constructorKey");
+ QTest::addColumn<QString>("setKey");
+
+ QTest::newRow("null, null") << QString() << QString();
+ QTest::newRow("null, one") << QString() << QString("one");
+ QTest::newRow("one, two") << QString("one") << QString("two");
+}
+
+/*!
+ Basic key testing
+ */
+void tst_QSystemSemaphore::key()
+{
+ QFETCH(QString, constructorKey);
+ QFETCH(QString, setKey);
+
+ QSystemSemaphore sem(constructorKey);
+ QCOMPARE(sem.key(), constructorKey);
+ QCOMPARE(sem.error(), QSystemSemaphore::NoError);
+ QCOMPARE(sem.errorString(), QString());
+
+ sem.setKey(setKey);
+ QCOMPARE(sem.key(), setKey);
+ QCOMPARE(sem.error(), QSystemSemaphore::NoError);
+ QCOMPARE(sem.errorString(), QString());
+}
+
+void tst_QSystemSemaphore::basicacquire()
+{
+ QSystemSemaphore sem("QSystemSemaphore_basicacquire", 1, QSystemSemaphore::Create);
+ QVERIFY(sem.acquire());
+ QCOMPARE(sem.error(), QSystemSemaphore::NoError);
+ QVERIFY(sem.release());
+ QCOMPARE(sem.error(), QSystemSemaphore::NoError);
+ QCOMPARE(sem.errorString(), QString());
+}
+
+void tst_QSystemSemaphore::complexacquire()
+{
+ QSystemSemaphore sem("QSystemSemaphore_complexacquire", 2, QSystemSemaphore::Create);
+ QVERIFY(sem.acquire());
+ QVERIFY(sem.release());
+ QVERIFY(sem.acquire());
+ QVERIFY(sem.release());
+ QVERIFY(sem.acquire());
+ QVERIFY(sem.acquire());
+ QVERIFY(sem.release());
+ QVERIFY(sem.release());
+ QCOMPARE(sem.error(), QSystemSemaphore::NoError);
+ QCOMPARE(sem.errorString(), QString());
+}
+
+void tst_QSystemSemaphore::basicProcesses()
+{
+#if defined (Q_OS_SYMBIAN) && defined(Q_CC_NOKIAX86)
+ QSKIP("Cannot launch multiple Qt processes in Symbian emulator", SkipAll);
+#endif
+ QSystemSemaphore sem("store", 0, QSystemSemaphore::Create);
+
+ QStringList acquireArguments = QStringList() << acquire_js();
+ QStringList releaseArguments = QStringList() << release_js();
+ QProcess acquire;
+ acquire.setProcessChannelMode(QProcess::ForwardedChannels);
+
+ QProcess release;
+ release.setProcessChannelMode(QProcess::ForwardedChannels);
+
+ acquire.start(LACKEYLOC "/lackey", acquireArguments);
+ acquire.waitForFinished(LACKYWAITTIME);
+ QVERIFY(acquire.state() == QProcess::Running);
+ acquire.kill();
+ release.start(LACKEYLOC "/lackey", releaseArguments);
+ acquire.waitForFinished(LACKYWAITTIME);
+ release.waitForFinished(LACKYWAITTIME);
+ QVERIFY(acquire.state() == QProcess::NotRunning);
+}
+
+void tst_QSystemSemaphore::processes_data()
+{
+ QTest::addColumn<int>("processes");
+ for (int i = 0; i < 5; ++i) {
+ QTest::newRow("1 process") << 1;
+ QTest::newRow("3 process") << 3;
+ QTest::newRow("10 process") << 10;
+ }
+}
+
+void tst_QSystemSemaphore::processes()
+{
+#if defined (Q_OS_SYMBIAN) && defined(Q_CC_NOKIAX86)
+ QSKIP("Cannot launch multiple Qt processes in Symbian emulator", SkipAll);
+#endif
+ QSystemSemaphore sem("store", 1, QSystemSemaphore::Create);
+
+ QFETCH(int, processes);
+ QStringList scripts;
+ for (int i = 0; i < processes; ++i)
+ scripts.append(acquirerelease_js());
+
+ QList<QProcess*> consumers;
+ for (int i = 0; i < scripts.count(); ++i) {
+ QStringList arguments = QStringList() << scripts.at(i);
+ QProcess *p = new QProcess;
+ p->setProcessChannelMode(QProcess::ForwardedChannels);
+ consumers.append(p);
+#ifdef Q_OS_WINCE
+ // We can't start the same executable twice on Windows CE.
+ // Create a copy instead.
+ QString lackeyCopy = QLatin1String(LACKEYLOC "/lackey");
+ if (i > 0) {
+ lackeyCopy.append(QString::number(i));
+ lackeyCopy.append(QLatin1String(".exe"));
+ if (!QFile::exists(lackeyCopy))
+ QVERIFY(QFile::copy(LACKEYLOC "/lackey.exe", lackeyCopy));
+ }
+ p->start(lackeyCopy, arguments);
+#else
+ p->start(LACKEYLOC "/lackey", arguments);
+#endif
+ }
+
+ while (!consumers.isEmpty()) {
+ consumers.first()->waitForFinished();
+ QCOMPARE(consumers.first()->exitStatus(), QProcess::NormalExit);
+ QCOMPARE(consumers.first()->exitCode(), 0);
+ delete consumers.takeFirst();
+ }
+}
+
+void tst_QSystemSemaphore::undo()
+{
+#if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN)
+ QSKIP("This test only checks a unix behavior", SkipSingle);
+#endif
+
+ QSystemSemaphore sem("store", 1, QSystemSemaphore::Create);
+
+ QStringList acquireArguments = QStringList() << acquire_js();
+ QProcess acquire;
+ acquire.setProcessChannelMode(QProcess::ForwardedChannels);
+ acquire.start(LACKEYLOC "/lackey", acquireArguments);
+ acquire.waitForFinished(LACKYWAITTIME);
+ QVERIFY(acquire.state()== QProcess::NotRunning);
+
+ // At process exit the kernel should auto undo
+
+ acquire.start(LACKEYLOC "/lackey", acquireArguments);
+ acquire.waitForFinished(LACKYWAITTIME);
+ QVERIFY(acquire.state()== QProcess::NotRunning);
+}
+
+void tst_QSystemSemaphore::initialValue()
+{
+#if defined (Q_OS_SYMBIAN) && defined(Q_CC_NOKIAX86)
+ QSKIP("Cannot launch multiple Qt processes in Symbian emulator", SkipAll);
+#endif
+ QSystemSemaphore sem("store", 1, QSystemSemaphore::Create);
+
+ QStringList acquireArguments = QStringList() << acquire_js();
+ QStringList releaseArguments = QStringList() << release_js();
+ QProcess acquire;
+ acquire.setProcessChannelMode(QProcess::ForwardedChannels);
+
+ QProcess release;
+ release.setProcessChannelMode(QProcess::ForwardedChannels);
+
+ acquire.start(LACKEYLOC "/lackey", acquireArguments);
+ acquire.waitForFinished(LACKYWAITTIME);
+ QVERIFY(acquire.state()== QProcess::NotRunning);
+
+ acquire.start(LACKEYLOC "/lackey", acquireArguments << "2");
+ acquire.waitForFinished(LACKYWAITTIME);
+ QVERIFY(acquire.state()== QProcess::Running);
+ acquire.kill();
+
+ release.start(LACKEYLOC "/lackey", releaseArguments);
+ acquire.waitForFinished(LACKYWAITTIME);
+ release.waitForFinished(LACKYWAITTIME);
+ QVERIFY(acquire.state()== QProcess::NotRunning);
+}
+QTEST_MAIN(tst_QSystemSemaphore)
+#include "tst_qsystemsemaphore.moc"
+
diff --git a/tests/auto/corelib/kernel/qtipc/qtipc.pro b/tests/auto/corelib/kernel/qtipc/qtipc.pro
new file mode 100644
index 0000000000..ac647e097b
--- /dev/null
+++ b/tests/auto/corelib/kernel/qtipc/qtipc.pro
@@ -0,0 +1,6 @@
+TEMPLATE=subdirs
+# lackey depends on qtscript and the tests depend on lackey :(
+#SUBDIRS=\
+# qsharedmemory \
+# qsystemsemaphore \
+# lackey
diff --git a/tests/auto/corelib/kernel/qtranslator/.gitignore b/tests/auto/corelib/kernel/qtranslator/.gitignore
new file mode 100644
index 0000000000..ce24e1016b
--- /dev/null
+++ b/tests/auto/corelib/kernel/qtranslator/.gitignore
@@ -0,0 +1 @@
+tst_qtranslator
diff --git a/tests/auto/corelib/kernel/qtranslator/hellotr_la.qm b/tests/auto/corelib/kernel/qtranslator/hellotr_la.qm
new file mode 100644
index 0000000000..cc42afe05c
--- /dev/null
+++ b/tests/auto/corelib/kernel/qtranslator/hellotr_la.qm
Binary files differ
diff --git a/tests/auto/corelib/kernel/qtranslator/hellotr_la.ts b/tests/auto/corelib/kernel/qtranslator/hellotr_la.ts
new file mode 100644
index 0000000000..2a4a41b9ea
--- /dev/null
+++ b/tests/auto/corelib/kernel/qtranslator/hellotr_la.ts
@@ -0,0 +1,16 @@
+<!DOCTYPE TS><TS version="1.1" language="de">
+<context>
+ <name>QPushButton</name>
+ <message>
+ <source>Hello world!</source>
+ <translation>Hallo Welt!</translation>
+ </message>
+ <message numerus="yes">
+ <source>Hello %n world(s)!</source>
+ <translation>
+ <numerusform>Hallo %n Welt!</numerusform>
+ <numerusform>Hallo %n Welten!</numerusform>
+ </translation>
+ </message>
+</context>
+</TS>
diff --git a/tests/auto/corelib/kernel/qtranslator/msgfmt_from_po.qm b/tests/auto/corelib/kernel/qtranslator/msgfmt_from_po.qm
new file mode 100644
index 0000000000..7b62819459
--- /dev/null
+++ b/tests/auto/corelib/kernel/qtranslator/msgfmt_from_po.qm
Binary files differ
diff --git a/tests/auto/corelib/kernel/qtranslator/qtranslator.pro b/tests/auto/corelib/kernel/qtranslator/qtranslator.pro
new file mode 100644
index 0000000000..171216afb4
--- /dev/null
+++ b/tests/auto/corelib/kernel/qtranslator/qtranslator.pro
@@ -0,0 +1,12 @@
+load(qttest_p4)
+QT += widgets
+SOURCES += tst_qtranslator.cpp
+RESOURCES += qtranslator.qrc
+
+wince*|symbian: {
+ addFiles.files = hellotr_la.qm msgfmt_from_po.qm
+ addFiles.path = .
+ DEPLOYMENT += addFiles
+}
+
+CONFIG += insignificant_test # QTBUG-21125
diff --git a/tests/auto/corelib/kernel/qtranslator/qtranslator.qrc b/tests/auto/corelib/kernel/qtranslator/qtranslator.qrc
new file mode 100644
index 0000000000..333dcfaa21
--- /dev/null
+++ b/tests/auto/corelib/kernel/qtranslator/qtranslator.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/tst_qtranslator">
+ <file>hellotr_la.qm</file>
+ </qresource>
+</RCC>
diff --git a/tests/auto/corelib/kernel/qtranslator/tst_qtranslator.cpp b/tests/auto/corelib/kernel/qtranslator/tst_qtranslator.cpp
new file mode 100644
index 0000000000..8e2ed157a4
--- /dev/null
+++ b/tests/auto/corelib/kernel/qtranslator/tst_qtranslator.cpp
@@ -0,0 +1,260 @@
+/****************************************************************************
+**
+** 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 <QWidget>
+#include <qtranslator.h>
+#include <qfile.h>
+
+//TESTED_CLASS=
+//TESTED_FILES=
+
+class tst_QTranslator : public QWidget
+{
+ Q_OBJECT
+
+public:
+ tst_QTranslator();
+ virtual ~tst_QTranslator();
+
+public slots:
+ void init();
+ void cleanup();
+
+protected:
+ bool event(QEvent *event);
+
+private slots:
+ void load();
+ void load2();
+ void threadLoad();
+ void testLanguageChange();
+ void plural();
+ void translate_qm_file_generated_with_msgfmt();
+ void loadFromResource();
+ void loadDirectory();
+
+private:
+ int languageChangeEventCounter;
+};
+
+
+tst_QTranslator::tst_QTranslator()
+ : languageChangeEventCounter(0)
+{
+ show();
+ hide();
+}
+
+tst_QTranslator::~tst_QTranslator()
+{
+}
+
+void tst_QTranslator::init()
+{
+}
+
+void tst_QTranslator::cleanup()
+{
+}
+
+bool tst_QTranslator::event(QEvent *event)
+{
+ if (event->type() == QEvent::LanguageChange)
+ ++languageChangeEventCounter;
+ return QWidget::event(event);
+}
+
+void tst_QTranslator::load()
+{
+
+ QTranslator tor( 0 );
+ tor.load("hellotr_la");
+ QVERIFY(!tor.isEmpty());
+ QCOMPARE(tor.translate("QPushButton", "Hello world!"), QString::fromLatin1("Hallo Welt!"));
+}
+
+void tst_QTranslator::load2()
+{
+ QTranslator tor( 0 );
+ QFile file("hellotr_la.qm");
+ file.open(QFile::ReadOnly);
+ QByteArray data = file.readAll();
+ tor.load((const uchar *)data.constData(), data.length());
+ QVERIFY(!tor.isEmpty());
+ QCOMPARE(tor.translate("QPushButton", "Hello world!"), QString::fromLatin1("Hallo Welt!"));
+}
+
+class TranslatorThread : public QThread
+{
+ void run() {
+ QTranslator tor( 0 );
+ tor.load("hellotr_la");
+
+ if (tor.isEmpty())
+ qFatal("Could not load translation");
+ if (tor.translate("QPushButton", "Hello world!") != QString::fromLatin1("Hallo Welt!"))
+ qFatal("Test string was not translated correctlys");
+ }
+};
+
+
+void tst_QTranslator::threadLoad()
+{
+ TranslatorThread thread;
+ thread.start();
+ QVERIFY(thread.wait(10 * 1000));
+}
+
+void tst_QTranslator::testLanguageChange()
+{
+ languageChangeEventCounter = 0;
+
+ QTranslator *tor = new QTranslator;
+ tor->load("hellotr_la.qm");
+ qApp->sendPostedEvents();
+ qApp->sendPostedEvents();
+ QCOMPARE(languageChangeEventCounter, 0);
+
+ tor->load("doesn't exist, same as clearing");
+ qApp->sendPostedEvents();
+ qApp->sendPostedEvents();
+ QCOMPARE(languageChangeEventCounter, 0);
+
+ tor->load("hellotr_la.qm");
+ qApp->sendPostedEvents();
+ qApp->sendPostedEvents();
+ QCOMPARE(languageChangeEventCounter, 0);
+
+ qApp->installTranslator(tor);
+ qApp->sendPostedEvents();
+ qApp->sendPostedEvents();
+ QCOMPARE(languageChangeEventCounter, 1);
+
+ tor->load("doesn't exist, same as clearing");
+ qApp->sendPostedEvents();
+ qApp->sendPostedEvents();
+ QCOMPARE(languageChangeEventCounter, 2);
+
+ tor->load("hellotr_la.qm");
+ qApp->sendPostedEvents();
+ qApp->sendPostedEvents();
+ QCOMPARE(languageChangeEventCounter, 3);
+
+ qApp->removeTranslator(tor);
+ qApp->sendPostedEvents();
+ qApp->sendPostedEvents();
+ QCOMPARE(languageChangeEventCounter, 4);
+
+ tor->load("doesn't exist, same as clearing");
+ qApp->sendPostedEvents();
+ qApp->sendPostedEvents();
+ QCOMPARE(languageChangeEventCounter, 4);
+
+ qApp->installTranslator(tor);
+ qApp->sendPostedEvents();
+ qApp->sendPostedEvents();
+ QCOMPARE(languageChangeEventCounter, 4);
+
+ tor->load("hellotr_la.qm");
+ qApp->sendPostedEvents();
+ qApp->sendPostedEvents();
+ QCOMPARE(languageChangeEventCounter, 5);
+
+ delete tor;
+ tor = 0;
+ qApp->sendPostedEvents();
+ qApp->sendPostedEvents();
+ QCOMPARE(languageChangeEventCounter, 6);
+}
+
+
+void tst_QTranslator::plural()
+{
+
+ QTranslator tor( 0 );
+ tor.load("hellotr_la");
+ QVERIFY(!tor.isEmpty());
+ QCoreApplication::installTranslator(&tor);
+ QCoreApplication::Encoding e = QCoreApplication::UnicodeUTF8;
+ QCOMPARE(QCoreApplication::translate("QPushButton", "Hello %n world(s)!", 0, e, 0), QString::fromLatin1("Hallo 0 Welten!"));
+ QCOMPARE(QCoreApplication::translate("QPushButton", "Hello %n world(s)!", 0, e, 1), QString::fromLatin1("Hallo 1 Welt!"));
+ QCOMPARE(QCoreApplication::translate("QPushButton", "Hello %n world(s)!", 0, e, 2), QString::fromLatin1("Hallo 2 Welten!"));
+}
+
+void tst_QTranslator::translate_qm_file_generated_with_msgfmt()
+{
+ QTranslator translator;
+ translator.load("msgfmt_from_po");
+ qApp->installTranslator(&translator);
+
+ QCOMPARE(QCoreApplication::translate("", "Intro"), QLatin1String("Einleitung"));
+ // The file is converted from a po file, thus it does not have any context info.
+ // The following should then not be translated
+ QCOMPARE(QCoreApplication::translate("contekst", "Intro"), QLatin1String("Intro"));
+ QCOMPARE(QCoreApplication::translate("contekst", "Intro\0\0"), QLatin1String("Intro"));
+ QCOMPARE(QCoreApplication::translate("contekst", "Intro\0x"), QLatin1String("Intro"));
+ QCOMPARE(QCoreApplication::translate("", "Intro\0\0"), QLatin1String("Einleitung"));
+ QCOMPARE(QCoreApplication::translate("", "Intro\0x"), QLatin1String("Einleitung"));
+
+ qApp->removeTranslator(&translator);
+}
+
+void tst_QTranslator::loadFromResource()
+{
+ QTranslator tor;
+ tor.load(":/tst_qtranslator/hellotr_la.qm");
+ QVERIFY(!tor.isEmpty());
+ QCOMPARE(tor.translate("QPushButton", "Hello world!"), QString::fromLatin1("Hallo Welt!"));
+}
+
+void tst_QTranslator::loadDirectory()
+{
+ QVERIFY(QFileInfo("../qtranslator").isDir());
+
+ QTranslator tor;
+ tor.load("qtranslator", "..");
+ QVERIFY(tor.isEmpty());
+}
+
+QTEST_MAIN(tst_QTranslator)
+#include "tst_qtranslator.moc"
diff --git a/tests/auto/corelib/kernel/qvariant/.gitignore b/tests/auto/corelib/kernel/qvariant/.gitignore
new file mode 100644
index 0000000000..2434ec945d
--- /dev/null
+++ b/tests/auto/corelib/kernel/qvariant/.gitignore
@@ -0,0 +1 @@
+tst_qvariant
diff --git a/tests/auto/corelib/kernel/qvariant/qvariant.pro b/tests/auto/corelib/kernel/qvariant/qvariant.pro
new file mode 100644
index 0000000000..52af24ea85
--- /dev/null
+++ b/tests/auto/corelib/kernel/qvariant/qvariant.pro
@@ -0,0 +1,5 @@
+load(qttest_p4)
+SOURCES += tst_qvariant.cpp
+QT += widgets network
+
+CONFIG+=insignificant_test
diff --git a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp
new file mode 100644
index 0000000000..25a32cc734
--- /dev/null
+++ b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp
@@ -0,0 +1,3235 @@
+/****************************************************************************
+**
+** 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 <qvariant.h>
+#include <qbitarray.h>
+#include <qhostaddress.h>
+#include <qdatetime.h>
+#include <qmap.h>
+#include <qiodevice.h>
+#include <qurl.h>
+#include <qlocale.h>
+#include <qkeysequence.h>
+#include <qbitmap.h>
+#include <qcursor.h>
+#include <qimage.h>
+#include <qicon.h>
+#include <qmatrix.h>