blob: 63d06497ce4297b40d7f0b8a029e7d492bad469f [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2015 Olivier Goffart <ogoffart@woboq.com>
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtTest/QtTest>
#include <qcoreapplication.h>
#include <qpointer.h>
#include <qtimer.h>
#include <qregularexpression.h>
#include <qmetaobject.h>
#include <qvariant.h>
#include <QTcpServer>
#include <QTcpSocket>
#include <QThread>
#include <QMutex>
#include <QWaitCondition>
#include <QScopedPointer>
#if QT_CONFIG(process)
# include <QProcess>
#endif
#include "qobject.h"
#ifdef QT_BUILD_INTERNAL
#include <private/qobject_p.h>
#endif
#include <math.h>
class tst_QObject : public QObject
{
Q_OBJECT
private slots:
void disconnect();
void connectSlotsByName();
void connectSignalsToSignalsWithDefaultArguments();
void receivers();
void normalize();
void qobject_castTemplate();
void findChildren();
void connectDisconnectNotify_data();
void connectDisconnectNotify();
void connectDisconnectNotifyPMF();
void disconnectNotify_receiverDestroyed();
void disconnectNotify_metaObjConnection();
void connectNotify_connectSlotsByName();
void connectDisconnectNotify_shadowing();
void emitInDefinedOrder();
void customTypes();
void streamCustomTypes();
void metamethod();
void namespaces();
void threadSignalEmissionCrash();
void thread();
void thread0();
void moveToThread();
void senderTest();
void declareInterface();
void qpointerResetBeforeDestroyedSignal();
#ifndef QT_NO_USERDATA
void testUserData();
#endif
void childDeletesItsSibling();
void dynamicProperties();
void floatProperty();
void qrealProperty();
void property();
void recursiveSignalEmission();
void signalBlocking();
void blockingQueuedConnection();
void childEvents();
void installEventFilter();
void deleteSelfInSlot();
void disconnectSelfInSlotAndDeleteAfterEmit();
void dumpObjectInfo();
void connectToSender();
void qobjectConstCast();
void uniqConnection();
void uniqConnectionPtr();
void interfaceIid();
void deleteQObjectWhenDeletingEvent();
void overloads();
void isSignalConnected();
void isSignalConnectedAfterDisconnection();
void qMetaObjectConnect();
void qMetaObjectDisconnectOne();
void sameName();
void connectByMetaMethods();
void connectByMetaMethodSlotInsteadOfSignal();
void connectConstructorByMetaMethod();
void disconnectByMetaMethod();
void disconnectNotSignalMetaMethod();
void autoConnectionBehavior();
void baseDestroyed();
void pointerConnect();
void pointerDisconnect();
void emitInDefinedOrderPointer();
void customTypesPointer();
void connectCxx0x();
void connectToStaticCxx0x();
void connectCxx0xTypeMatching();
void connectCxx17Noexcept();
void connectConvert();
void connectWithReference();
void connectManyArguments();
void connectForwardDeclare();
void connectNoDefaultConstructorArg();
void returnValue_data();
void returnValue();
void returnValue2_data();
void returnValue2();
void connectVirtualSlots();
void connectSlotsVMIClass(); // VMI = Virtual or Multiple Inheritance
void connectPrivateSlots();
void connectFunctorArgDifference();
void connectFunctorOverloads();
void connectFunctorQueued();
void connectFunctorWithContext();
void connectFunctorWithContextUnique();
void connectFunctorDeadlock();
void connectFunctorMoveOnly();
void connectStaticSlotWithObject();
void disconnectDoesNotLeakFunctor();
void contextDoesNotLeakFunctor();
void connectBase();
void connectWarnings();
void qmlConnect();
void exceptions();
void noDeclarativeParentChangedOnDestruction();
void deleteLaterInAboutToBlockHandler();
void mutableFunctor();
void checkArgumentsForNarrowing();
void nullReceiver();
void functorReferencesConnection();
void disconnectDisconnects();
};
struct QObjectCreatedOnShutdown
{
QObjectCreatedOnShutdown() {}
~QObjectCreatedOnShutdown()
{
QObject();
}
};
static QObjectCreatedOnShutdown s_qobjectCreatedOnShutdown;
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();
void signal6(void);
void signal7(int, const QString &);
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;
static void playWithObjects()
{
// Do operations that will lock the internal signalSlotLock mutex on many QObjects.
// The more QObjects, the higher the chance that the signalSlotLock mutex used
// is already in use. If the number of objects is higher than the number of mutexes in
// the pool (currently 131), the deadlock should always trigger. Use an even higher number
// to be on the safe side.
const int objectCount = 1024;
SenderObject lotsOfObjects[objectCount];
for (int i = 0; i < objectCount; ++i) {
QObject::connect(&lotsOfObjects[i], &SenderObject::signal1,
&lotsOfObjects[i], &SenderObject::aPublicSlot);
}
}
void tst_QObject::disconnect()
{
SenderObject s;
ReceiverObject r1;
ReceiverObject r2;
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();
QVERIFY(r1.called(1));
QVERIFY(r1.called(2));
QVERIFY(r1.called(3));
QVERIFY(r1.called(4));
r1.reset();
// usual disconnect with all parameters given
bool ret = QObject::disconnect(&s, SIGNAL(signal1()), &r1, SLOT(slot1()));
s.emitSignal1();
QVERIFY(!r1.called(1));
r1.reset();
QVERIFY(ret);
ret = QObject::disconnect(&s, SIGNAL(signal1()), &r1, SLOT(slot1()));
QVERIFY(!ret);
// disconnect all signals from s from all slots from r1
QObject::disconnect(&s, 0, &r1, 0);
s.emitSignal2();
s.emitSignal3();
s.emitSignal4();
QVERIFY(!r1.called(2));
QVERIFY(!r1.called(3));
QVERIFY(!r1.called(4));
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();
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, 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();
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);
QVERIFY(!r1.called(2));
QVERIFY(!r2.called(2));
QVERIFY(!r1.called(2));
QVERIFY(!r2.called(2));
}
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 emitSignalManyParams(int i1, int i2, int i3, QString string, bool onoff, bool dummy) { emit signalManyParams(i1, i2, i3, string, onoff, dummy); }
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:
QList<int> called_slots;
AutoConnectReceiver()
{
connect(this, SIGNAL(on_Sender_signalLoopBack()), this, SLOT(slotLoopBack()));
}
void emitSignalNoParams() { emit signalNoParams(); }
void emit_signal_with_underscore() { emit signal_with_underscore(); }
public slots:
void on_Sender_signalNoParams() { called_slots << 1; }
void on_Sender_signalWithParams(int i = 0) { called_slots << 2; Q_UNUSED(i); }
void on_Sender_signalWithParams(int i, QString string) { called_slots << 3; Q_UNUSED(i);Q_UNUSED(string); }
void on_Sender_signalManyParams() { called_slots << 4; }
void on_Sender_signalManyParams(int i1, int i2, int i3, QString string, bool onoff)
{ called_slots << 5; 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_slots << 6; 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_slots << 7; Q_UNUSED(i1);Q_UNUSED(i2);Q_UNUSED(i3);Q_UNUSED(string);Q_UNUSED(onoff); }
void slotLoopBack() { called_slots << 8; }
void on_Receiver_signalNoParams() { called_slots << 9; }
void on_Receiver_signal_with_underscore() { called_slots << 10; }
protected slots:
void o() { called_slots << -1; }
void on() { called_slots << -1; }
void on_() { called_slots << -1; }
void on_something() { called_slots << -1; }
void on_child_signal() { called_slots << -1; }
signals:
void on_Sender_signalLoopBack();
void signalNoParams();
void signal_with_underscore();
};
void tst_QObject::connectSlotsByName()
{
AutoConnectReceiver receiver;
receiver.setObjectName("Receiver");
AutoConnectSender sender(&receiver);
sender.setObjectName("Sender");
QTest::ignoreMessage(QtWarningMsg, "QMetaObject::connectSlotsByName: No matching signal for on_child_signal()");
QTest::ignoreMessage(QtWarningMsg, "QMetaObject::connectSlotsByName: Connecting slot on_Sender_signalManyParams() with the first of the following compatible signals: (\"signalManyParams(int,int,int,QString,bool)\", \"signalManyParams(int,int,int,QString,bool,bool)\")");
QMetaObject::connectSlotsByName(&receiver);
receiver.called_slots.clear();
sender.emitSignalNoParams();
QCOMPARE(receiver.called_slots, QList<int>() << 1);
receiver.called_slots.clear();
sender.emitSignalWithParams(0);
QCOMPARE(receiver.called_slots, QList<int>() << 2);
receiver.called_slots.clear();
sender.emitSignalWithParams(0, "string");
QCOMPARE(receiver.called_slots, QList<int>() << 3);
receiver.called_slots.clear();
sender.emitSignalManyParams(1, 2, 3, "string", true);
sender.emitSignalManyParams(1, 2, 3, "string", true, false);
// the slot '4' (signalManyParams()) will get connected
// to either of the two signalManyParams(...) overloads
QCOMPARE(receiver.called_slots, QList<int>() << 4 << 5 << 6);
receiver.called_slots.clear();
sender.emitSignalManyParams2(1, 2, 3, "string", true);
QCOMPARE(receiver.called_slots, QList<int>() << 7);
receiver.called_slots.clear();
sender.emitSignalLoopBack();
QCOMPARE(receiver.called_slots, QList<int>() << 8);
receiver.called_slots.clear();
receiver.emitSignalNoParams();
QCOMPARE(receiver.called_slots, QList<int>() << 9);
receiver.called_slots.clear();
receiver.emit_signal_with_underscore();
QCOMPARE(receiver.called_slots, QList<int>() << 10);
}
void tst_QObject::qobject_castTemplate()
{
QScopedPointer<QObject> o;
QVERIFY(!::qobject_cast<QObject*>(o.data()));
o.reset(new SenderObject);
QVERIFY(::qobject_cast<SenderObject*>(o.data()));
QVERIFY(::qobject_cast<QObject*>(o.data()));
QVERIFY(!::qobject_cast<ReceiverObject*>(o.data()));
}
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);
Q_SET_OBJECT_NAME(o);
Q_SET_OBJECT_NAME(o1);
Q_SET_OBJECT_NAME(o2);
Q_SET_OBJECT_NAME(o11);
Q_SET_OBJECT_NAME(o12);
Q_SET_OBJECT_NAME(o111);
Q_SET_OBJECT_NAME(t1);
Q_SET_OBJECT_NAME(t121);
emptyname.setObjectName("");
QObject *op = 0;
op = o.findChild<QObject*>("o1");
QCOMPARE(op, &o1);
op = o.findChild<QObject*>("o2");
QCOMPARE(op, &o2);
op = o.findChild<QObject*>("o11");
QCOMPARE(op, &o11);
op = o.findChild<QObject*>("o12");
QCOMPARE(op, &o12);
op = o.findChild<QObject*>("o111");
QCOMPARE(op, &o111);
op = o.findChild<QObject*>("t1");
QCOMPARE(op, static_cast<QObject *>(&t1));
op = o.findChild<QObject*>("t121");
QCOMPARE(op, static_cast<QObject *>(&t121));
op = o.findChild<QTimer*>("t1");
QCOMPARE(op, static_cast<QObject *>(&t1));
op = o.findChild<QTimer*>("t121");
QCOMPARE(op, static_cast<QObject *>(&t121));
op = o.findChild<QTimer*>("o12");
QCOMPARE(op, static_cast<QObject *>(0));
op = o.findChild<QObject*>("o");
QCOMPARE(op, static_cast<QObject *>(0));
op = o.findChild<QObject*>("harry");
QCOMPARE(op, static_cast<QObject *>(0));
op = o.findChild<QObject*>("o1");
QCOMPARE(op, &o1);
QList<QObject*> l;
QList<QTimer*> tl;
l = o.findChildren<QObject*>("o1");
QCOMPARE(l.size(), 1);
QCOMPARE(l.at(0), &o1);
l = o.findChildren<QObject*>("o2");
QCOMPARE(l.size(), 1);
QCOMPARE(l.at(0), &o2);
l = o.findChildren<QObject*>("o11");
QCOMPARE(l.size(), 1);
QCOMPARE(l.at(0), &o11);
l = o.findChildren<QObject*>("o12");
QCOMPARE(l.size(), 1);
QCOMPARE(l.at(0), &o12);
l = o.findChildren<QObject*>("o111");
QCOMPARE(l.size(), 1);
QCOMPARE(l.at(0), &o111);
l = o.findChildren<QObject*>("t1");
QCOMPARE(l.size(), 1);
QCOMPARE(l.at(0), static_cast<QObject *>(&t1));
l = o.findChildren<QObject*>("t121");
QCOMPARE(l.size(), 1);
QCOMPARE(l.at(0), static_cast<QObject *>(&t121));
tl = o.findChildren<QTimer*>("t1");
QCOMPARE(tl.size(), 1);
QCOMPARE(tl.at(0), &t1);
tl = o.findChildren<QTimer*>("t121");
QCOMPARE(tl.size(), 1);
QCOMPARE(tl.at(0), &t121);
l = o.findChildren<QObject*>("o");
QCOMPARE(l.size(), 0);
l = o.findChildren<QObject*>("harry");
QCOMPARE(l.size(), 0);
tl = o.findChildren<QTimer*>("o12");
QCOMPARE(tl.size(), 0);
l = o.findChildren<QObject*>("o1");
QCOMPARE(l.size(), 1);
QCOMPARE(l.at(0), &o1);
l = o.findChildren<QObject*>(QRegularExpression("^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 = o.findChildren<QObject*>(QRegularExpression("t.*"));
QCOMPARE(l.size(), 2);
QVERIFY(l.contains(&t1));
QVERIFY(l.contains(&t121));
tl = o.findChildren<QTimer*>(QRegularExpression("^.*$"));
QCOMPARE(tl.size(), 3);
QVERIFY(tl.contains(&t1));
QVERIFY(tl.contains(&t121));
tl = o.findChildren<QTimer*>(QRegularExpression("^o.*$"));
QCOMPARE(tl.size(), 0);
l = o.findChildren<QObject*>(QRegularExpression("^harry$"));
QCOMPARE(l.size(), 0);
l = o.findChildren<QObject*>(QRegularExpression("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 = o.findChildren<QObject*>(QRegularExpression("t.*"));
QCOMPARE(l.size(), 2);
QVERIFY(l.contains(&t1));
QVERIFY(l.contains(&t121));
tl = o.findChildren<QTimer*>(QRegularExpression(".*"));
QCOMPARE(tl.size(), 3);
QVERIFY(tl.contains(&t1));
QVERIFY(tl.contains(&t121));
tl = o.findChildren<QTimer*>(QRegularExpression("o.*"));
QCOMPARE(tl.size(), 0);
l = o.findChildren<QObject*>(QRegularExpression("harry"));
QCOMPARE(l.size(), 0);
// empty and null string check
op = o.findChild<QObject*>();
QCOMPARE(op, &o1);
op = o.findChild<QObject*>("");
QCOMPARE(op, &unnamed);
op = o.findChild<QObject*>("unnamed");
QCOMPARE(op, static_cast<QObject *>(0));
l = o.findChildren<QObject*>();
QCOMPARE(l.size(), 9);
l = o.findChildren<QObject*>("");
QCOMPARE(l.size(), 2);
l = o.findChildren<QObject*>("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*>(QRegularExpression("^o.*$"), Qt::FindDirectChildrenOnly);
QCOMPARE(l.size(), 2);
QVERIFY(l.contains(&o1));
QVERIFY(l.contains(&o2));
l = o.findChildren<QObject*>(QRegularExpression("^t.*$"), Qt::FindDirectChildrenOnly);
QCOMPARE(l.size(), 1);
QVERIFY(l.contains(&t1));
tl = o.findChildren<QTimer*>(QRegularExpression("^.*$"), Qt::FindDirectChildrenOnly);
QCOMPARE(tl.size(), 2);
QVERIFY(tl.contains(&t1));
tl = o.findChildren<QTimer*>(QRegularExpression("^o.*$"), Qt::FindDirectChildrenOnly);
QCOMPARE(tl.size(), 0);
l = o.findChildren<QObject*>(QRegularExpression("^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()
{}
QList<QMetaMethod> connectedSignals;
QList<QMetaMethod> disconnectedSignals;
void clearNotifications()
{
connectedSignals.clear();
disconnectedSignals.clear();
}
protected:
void connectNotify(const QMetaMethod &signal)
{ connectedSignals.append(signal); }
void disconnectNotify(const QMetaMethod &signal)
{ disconnectedSignals.append(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 ) );
QTest::newRow("combo5") << SIGNAL( signal6( void ) ) << SLOT( slot4() );
QTest::newRow("combo6") << SIGNAL( signal6() ) << SLOT( slot4() );
QTest::newRow("combo7") << SIGNAL( signal7( int , const QString & ) ) << SLOT( slot4() );
}
void tst_QObject::connectDisconnectNotify()
{
NotifyObject s;
NotifyObject r;
QFETCH(QString, a_signal);
QFETCH(QString, a_slot);
// 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);
QVERIFY(signal.isValid());
QVERIFY(method.isValid());
// Test connectNotify
QVERIFY(QObject::connect((SenderObject *)&s, a_signal.toLatin1(),
(ReceiverObject *)&r, a_slot.toLatin1()));
QCOMPARE(s.connectedSignals.size(), 1);
QCOMPARE(s.connectedSignals.at(0), signal);
QVERIFY(s.disconnectedSignals.isEmpty());
// Test disconnectNotify
QVERIFY(QObject::disconnect((SenderObject *)&s, a_signal.toLatin1(),
(ReceiverObject *)&r, a_slot.toLatin1()));
QCOMPARE(s.disconnectedSignals.size(), 1);
QCOMPARE(s.disconnectedSignals.at(0), signal);
QCOMPARE(s.connectedSignals.size(), 1);
// Reconnect
s.clearNotifications();
QVERIFY(QObject::connect((SenderObject *)&s, a_signal.toLatin1(),
(ReceiverObject *)&r, a_slot.toLatin1()));
QCOMPARE(s.connectedSignals.size(), 1);
QCOMPARE(s.connectedSignals.at(0), signal);
QVERIFY(s.disconnectedSignals.isEmpty());
// Test disconnectNotify for a complete disconnect
QVERIFY(((SenderObject *)&s)->disconnect((ReceiverObject *)&r));
QCOMPARE(s.disconnectedSignals.size(), 1);
QCOMPARE(s.disconnectedSignals.at(0), QMetaMethod());
QCOMPARE(s.connectedSignals.size(), 1);
// Test connectNotify when connecting by QMetaMethod
s.clearNotifications();
QVERIFY(QObject::connect((SenderObject *)&s, signal, (ReceiverObject *)&r, method));
QCOMPARE(s.connectedSignals.size(), 1);
QCOMPARE(s.connectedSignals.at(0), signal);
QVERIFY(s.disconnectedSignals.isEmpty());
// Test disconnectNotify when disconnecting by QMetaMethod
QVERIFY(QObject::disconnect((SenderObject *)&s, signal, (ReceiverObject *)&r, method));
QCOMPARE(s.disconnectedSignals.size(), 1);
QCOMPARE(s.disconnectedSignals.at(0), signal);
QCOMPARE(s.connectedSignals.size(), 1);
// Reconnect
s.clearNotifications();
QVERIFY(QObject::connect((SenderObject *)&s, a_signal.toLatin1(),
(ReceiverObject *)&r, a_slot.toLatin1()));
// Test disconnectNotify for a complete disconnect by QMetaMethod
QVERIFY(QObject::disconnect((SenderObject *)&s, QMetaMethod(), 0, QMetaMethod()));
QCOMPARE(s.disconnectedSignals.size(), 1);
QCOMPARE(s.disconnectedSignals.at(0), QMetaMethod());
QCOMPARE(s.connectedSignals.size(), 1);
// Test connectNotify when connecting by index
s.clearNotifications();
QVERIFY(QMetaObject::connect((SenderObject *)&s, signalIndx, (ReceiverObject *)&r, methodIndx));
QCOMPARE(s.connectedSignals.size(), 1);
QCOMPARE(s.connectedSignals.at(0), signal);
QVERIFY(s.disconnectedSignals.isEmpty());
// Test disconnectNotify when disconnecting by index
QVERIFY(QMetaObject::disconnect((SenderObject *)&s, signalIndx,
(ReceiverObject *)&r, methodIndx));
QCOMPARE(s.disconnectedSignals.size(), 1);
QCOMPARE(s.disconnectedSignals.at(0), signal);
QCOMPARE(s.connectedSignals.size(), 1);
}
static void connectDisconnectNotifyTestSlot() {}
void tst_QObject::connectDisconnectNotifyPMF()
{
NotifyObject s;
NotifyObject r;
QMetaMethod signal = QMetaMethod::fromSignal(&SenderObject::signal1);
// Test connectNotify
QVERIFY(QObject::connect((SenderObject *)&s, &SenderObject::signal1,
(ReceiverObject *)&r, &ReceiverObject::slot1));
QCOMPARE(s.connectedSignals.size(), 1);
QCOMPARE(s.connectedSignals.at(0), signal);
QVERIFY(s.disconnectedSignals.isEmpty());
// Test disconnectNotify
QVERIFY(QObject::disconnect((SenderObject *)&s, &SenderObject::signal1,
(ReceiverObject *)&r, &ReceiverObject::slot1));
QCOMPARE(s.disconnectedSignals.size(), 1);
QCOMPARE(s.disconnectedSignals.at(0), signal);
QCOMPARE(s.connectedSignals.size(), 1);
// Reconnect
s.clearNotifications();
QVERIFY(QObject::connect((SenderObject *)&s, &SenderObject::signal1,
(ReceiverObject *)&r, &ReceiverObject::slot1));
QCOMPARE(s.connectedSignals.size(), 1);
QCOMPARE(s.connectedSignals.at(0), signal);
QVERIFY(s.disconnectedSignals.isEmpty());
// Test disconnectNotify with wildcard slot
QVERIFY(QObject::disconnect((SenderObject *)&s, &SenderObject::signal1,
(ReceiverObject *)&r, 0));
QCOMPARE(s.disconnectedSignals.size(), 1);
QCOMPARE(s.disconnectedSignals.at(0), signal);
QCOMPARE(s.connectedSignals.size(), 1);
// Reconnect
s.clearNotifications();
QMetaObject::Connection conn = connect((SenderObject *)&s, &SenderObject::signal1,
(ReceiverObject *)&r, &ReceiverObject::slot1);
QVERIFY(conn);
// Test disconnectNotify when disconnecting by QMetaObject::Connection
QVERIFY(QObject::disconnect(conn));
QVERIFY(!s.disconnectedSignals.isEmpty());
// Test connectNotify when connecting by function pointer
s.clearNotifications();
QVERIFY(QObject::connect((SenderObject *)&s, &SenderObject::signal1,
connectDisconnectNotifyTestSlot));
QCOMPARE(s.connectedSignals.size(), 1);
QCOMPARE(s.connectedSignals.at(0), signal);
QVERIFY(s.disconnectedSignals.isEmpty());
}
void tst_QObject::disconnectNotify_receiverDestroyed()
{
NotifyObject s;
{
NotifyObject r;
QVERIFY(QObject::connect((SenderObject *)&s, SIGNAL(signal1()),
(ReceiverObject *)&r, SLOT(slot1())));
}
QCOMPARE(s.disconnectedSignals.count(), 1);
QCOMPARE(s.disconnectedSignals.at(0), QMetaMethod::fromSignal(&SenderObject::signal1));
s.disconnectedSignals.clear();
{
NotifyObject r;
QVERIFY(QObject::connect((SenderObject *)&s, SIGNAL(signal3()),
(ReceiverObject *)&r, SLOT(slot3())));
}
QCOMPARE(s.disconnectedSignals.count(), 1);
QCOMPARE(s.disconnectedSignals.at(0), QMetaMethod::fromSignal(&SenderObject::signal3));
s.disconnectedSignals.clear();
{
NotifyObject r;
QVERIFY(QObject::connect((SenderObject *)&s, SIGNAL(destroyed()), (ReceiverObject *)&r, SLOT(slot3())));
}
QCOMPARE(s.disconnectedSignals.count(), 1);
QCOMPARE(s.disconnectedSignals.at(0), QMetaMethod::fromSignal(&QObject::destroyed));
}
void tst_QObject::disconnectNotify_metaObjConnection()
{
NotifyObject s;
{
NotifyObject r;
QMetaObject::Connection c = QObject::connect((SenderObject *)&s, SIGNAL(signal1()),
(ReceiverObject *)&r, SLOT(slot1()));
QVERIFY(c);
QVERIFY(QObject::disconnect(c));
QCOMPARE(s.disconnectedSignals.count(), 1);
QCOMPARE(s.disconnectedSignals.at(0), QMetaMethod::fromSignal(&SenderObject::signal1));
QCOMPARE(s.disconnectedSignals.count(), 1);
}
}
class ConnectByNameNotifySenderObject : public QObject
{
Q_OBJECT
public:
QList<QMetaMethod> connectedSignals;
QList<QMetaMethod> disconnectedSignals;
void clearNotifications()
{
connectedSignals.clear();
disconnectedSignals.clear();
}
protected:
void connectNotify(const QMetaMethod &signal)
{ connectedSignals.append(signal); }
void disconnectNotify(const QMetaMethod &signal)
{ disconnectedSignals.append(signal); }
Q_SIGNALS:
void signal1();
};
class ConnectByNameNotifyReceiverObject : public QObject
{
Q_OBJECT
void createNotifyChild(const char *name)
{
QObject *o = new ConnectByNameNotifySenderObject;
o->setParent(this);
o->setObjectName(QString::fromLatin1(name));
}
public:
ConnectByNameNotifyReceiverObject()
{
createNotifyChild("foo");
createNotifyChild("bar");
createNotifyChild("baz");
};
public Q_SLOTS:
void on_foo_signal1() {}
void on_bar_signal1() {}
void on_baz_signal1() {}
};
void tst_QObject::connectNotify_connectSlotsByName()
{
ConnectByNameNotifyReceiverObject testObject;
const QList<ConnectByNameNotifySenderObject *> senders =
testObject.findChildren<ConnectByNameNotifySenderObject *>();
for (ConnectByNameNotifySenderObject *o : senders) {
QVERIFY(o->connectedSignals.isEmpty());
QVERIFY(o->disconnectedSignals.isEmpty());
}
QMetaObject::connectSlotsByName(&testObject);
for (ConnectByNameNotifySenderObject *o : senders) {
QCOMPARE(o->connectedSignals.size(), 1);
QCOMPARE(o->connectedSignals.at(0), QMetaMethod::fromSignal(&ConnectByNameNotifySenderObject::signal1));
QVERIFY(o->disconnectedSignals.isEmpty());
}
}
class ConnectDisconnectNotifyShadowObject
: public ConnectByNameNotifySenderObject
{
Q_OBJECT
public Q_SLOTS:
void slot1() {}
Q_SIGNALS:
void signal1();
};
void tst_QObject::connectDisconnectNotify_shadowing()
{
ConnectDisconnectNotifyShadowObject s;
// Obtain QMetaMethods
QMetaMethod shadowedSignal1 = QMetaMethod::fromSignal(&ConnectByNameNotifySenderObject::signal1);
QMetaMethod redefinedSignal1 = QMetaMethod::fromSignal(&ConnectDisconnectNotifyShadowObject::signal1);
QVERIFY(shadowedSignal1 != redefinedSignal1);
int slot1Index = s.metaObject()->indexOfSlot("slot1()");
QVERIFY(slot1Index != -1);
QMetaMethod slot1 = s.metaObject()->method(slot1Index);
// Test connectNotify
#ifndef QT_NO_DEBUG
const char *warning = "QMetaObject::indexOfSignal: signal signal1() from "
"ConnectByNameNotifySenderObject redefined in "
"ConnectDisconnectNotifyShadowObject";
QTest::ignoreMessage(QtWarningMsg, warning);
#endif
QVERIFY(QObject::connect(&s, SIGNAL(signal1()), &s, SLOT(slot1())));
QCOMPARE(s.connectedSignals.size(), 1);
QCOMPARE(s.connectedSignals.at(0), redefinedSignal1);
QVERIFY(s.disconnectedSignals.isEmpty());
// Test disconnectNotify
#ifndef QT_NO_DEBUG
QTest::ignoreMessage(QtWarningMsg, warning);
#endif
QVERIFY(QObject::disconnect(&s, SIGNAL(signal1()), &s, SLOT(slot1())));
QCOMPARE(s.disconnectedSignals.size(), 1);
QCOMPARE(s.disconnectedSignals.at(0), redefinedSignal1);
QCOMPARE(s.connectedSignals.size(), 1);
// Test connectNotify when connecting by shadowed QMetaMethod
s.clearNotifications();
QVERIFY(QObject::connect(&s, shadowedSignal1, &s, slot1));
QCOMPARE(s.connectedSignals.size(), 1);
QCOMPARE(s.connectedSignals.at(0), shadowedSignal1);
QVERIFY(s.disconnectedSignals.isEmpty());
// Test disconnectNotify when disconnecting by shadowed QMetaMethod
QVERIFY(QObject::disconnect(&s, shadowedSignal1, &s, slot1));
QCOMPARE(s.disconnectedSignals.size(), 1);
QCOMPARE(s.disconnectedSignals.at(0), shadowedSignal1);
QCOMPARE(s.connectedSignals.size(), 1);
// Test connectNotify when connecting by redefined QMetaMethod
s.clearNotifications();
QVERIFY(QObject::connect(&s, redefinedSignal1, &s, slot1));
QCOMPARE(s.connectedSignals.size(), 1);
QCOMPARE(s.connectedSignals.at(0), redefinedSignal1);
QVERIFY(s.disconnectedSignals.isEmpty());
// Test disconnectNotify when disconnecting by redefined QMetaMethod
QVERIFY(QObject::disconnect(&s, redefinedSignal1, &s, slot1));
QCOMPARE(s.disconnectedSignals.size(), 1);
QCOMPARE(s.disconnectedSignals.at(0), redefinedSignal1);
QCOMPARE(s.connectedSignals.size(), 1);
}
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();
QVERIFY(seq1.called(1));
QVERIFY(seq2.called(1));
QVERIFY(!seq3->called(1));
QVERIFY(seq4.called(1));
QVERIFY(seq1.called(2));
QVERIFY(seq2.called(2));
QVERIFY(!seq3->called(2));
QVERIFY(seq4.called(2));
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();
QVERIFY(seq1.called(2));
QVERIFY(seq2.called(2));
QVERIFY(!seq3->called(2));
QVERIFY(seq4.called(2));
QVERIFY(seq1.called(1));
QVERIFY(seq2.called(1));
QVERIFY(!seq3->called(1));
QVERIFY(seq4.called(1));
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();
QVERIFY(seq1.called(1));
QVERIFY(seq2.called(1));
QVERIFY(!seq3->called(1));
QVERIFY(seq4.called(1));
QVERIFY(seq1.called(2));
QVERIFY(seq2.called(2));
QVERIFY(!seq3->called(2));
QVERIFY(seq4.called(2));
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));
QVERIFY(seq1.called(1));
QVERIFY(seq2.called(1));
QVERIFY(seq4.called(1));
QVERIFY(seq1.called(2));
QVERIFY(seq2.called(2));
QVERIFY(!seq4.called(2));
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 CheckInstanceCount
{
const int saved;
CheckInstanceCount() : saved(instanceCount) {}
~CheckInstanceCount() { QCOMPARE(saved, instanceCount); }
};
struct CustomType
{
CustomType(int l1 = 0, int l2 = 0, int l3 = 0): i1(l1), i2(l2), i3(l3)
{ ++instanceCount; playWithObjects(); }
CustomType(const CustomType &other): i1(other.i1), i2(other.i2), i3(other.i3)
{ ++instanceCount; playWithObjects(); }
~CustomType() { --instanceCount; playWithObjects(); }
CustomType &operator=(const CustomType &) = default;
int i1, i2, i3;
int value() { return i1 + i2 + i3; }
};
Q_DECLARE_METATYPE(CustomType*)
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);
void slot2(const QList<CustomType> &ct);
signals:
void signal1(CustomType ct);
void signal2(const QList<CustomType> &ct);
public:
CustomType received;
};
void QCustomTypeChecker::slot1(CustomType ct)
{ received = ct; }
void QCustomTypeChecker::slot2(const QList< CustomType >& ct)
{ received = ct[0]; }
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_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_ENUM(Alpha)
Q_ENUM(Priority)
};
Q_DECLARE_METATYPE(PropertyObject::Priority)
void tst_QObject::threadSignalEmissionCrash()
{
int loopCount = 1000;
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() != nullptr);
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() != nullptr);
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 == nullptr);
QVERIFY(cpointer == nullptr);
}
{
// 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);
child->startTimer(90);
object->startTimer(100);
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();
}
// WinRT does not allow connection to localhost
#ifndef Q_OS_WINRT
{
// 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();
}
#endif
}
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);
QCOMPARE(QMetaType::Type(mo->property(idx).type()), QMetaType::QVariant);
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*");
qRegisterMetaType<CustomType*>();
QCOMPARE(property.type(), QVariant::UserType);
QCOMPARE(property.userType(), qMetaTypeId<CustomType*>());
CustomType *customPointer = 0;
QVariant customVariant = object.property("custom");
customPointer = qvariant_cast<CustomType *>(customVariant);
QCOMPARE(customPointer, object.custom());
CustomType custom;
customPointer = &custom;
customVariant.setValue(customPointer);
property = mo->property(mo->indexOfProperty("custom"));
QVERIFY(property.isWritable());
QCOMPARE(property.typeName(), "CustomType*");
QCOMPARE(property.type(), QVariant::UserType);
QCOMPARE(property.userType(), qMetaTypeId<CustomType*>());
QVERIFY(object.setProperty("custom", customVariant));
QCOMPARE(object.custom(), customPointer);
customVariant = object.property("custom");
customPointer = qvariant_cast<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(var.canConvert<PropertyObject::Priority>());
QCOMPARE(qvariant_cast<PropertyObject::Priority>(var), PropertyObject::High);
object.setPriority(PropertyObject::Low);
QCOMPARE(qvariant_cast<PropertyObject::Priority>(object.property("priority")), PropertyObject::Low);
QVERIFY(object.setProperty("priority", PropertyObject::VeryHigh));
QCOMPARE(qvariant_cast<PropertyObject::Priority>(object.property("priority")), PropertyObject::VeryHigh);
QVERIFY(object.setProperty("priority", "High"));
QCOMPARE(qvariant_cast<PropertyObject::Priority>(object.property("priority")), PropertyObject::High);
QVERIFY(!object.setProperty("priority", QVariant()));
var = object.property("priority");
QCOMPARE(qvariant_cast<PropertyObject::Priority>(var), PropertyObject::High);
object.setPriority(PropertyObject::Low);
QCOMPARE(qvariant_cast<PropertyObject::Priority>(object.property("priority")), PropertyObject::Low);
object.setProperty("priority", var);
QCOMPARE(qvariant_cast<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(m.methodSignature() == "invoke1()");
QCOMPARE(m.methodType(), QMetaMethod::Method);
QCOMPARE(m.access(), QMetaMethod::Public);
QVERIFY(!(m.attributes() & QMetaMethod::Scriptable));
QVERIFY(!(m.attributes() & QMetaMethod::Compatibility));
m = mobj->method(mobj->indexOfMethod("sinvoke1()"));
QVERIFY(m.methodSignature() == "sinvoke1()");
QCOMPARE(m.methodType(), QMetaMethod::Method);
QCOMPARE(m.access(), QMetaMethod::Public);
QVERIFY((m.attributes() & QMetaMethod::Scriptable));
QVERIFY(!(m.attributes() & QMetaMethod::Compatibility));
m = mobj->method(mobj->indexOfMethod("invoke2()"));
QVERIFY(m.methodSignature() == "invoke2()");
QCOMPARE(m.methodType(), QMetaMethod::Method);
QCOMPARE(m.access(), QMetaMethod::Protected);
QVERIFY(!(m.attributes() & QMetaMethod::Scriptable));
QVERIFY((m.attributes() & QMetaMethod::Compatibility));
m = mobj->method(mobj->indexOfMethod("sinvoke2()"));
QVERIFY(m.methodSignature() == "sinvoke2()");
QCOMPARE(m.methodType(), QMetaMethod::Method);
QCOMPARE(m.access(), QMetaMethod::Protected);
QVERIFY((m.attributes() & QMetaMethod::Scriptable));
QVERIFY((m.attributes() & QMetaMethod::Compatibility));
m = mobj->method(mobj->indexOfMethod("invoke3()"));
QVERIFY(m.methodSignature() == "invoke3()");
QCOMPARE(m.methodType(), QMetaMethod::Method);
QCOMPARE(m.access(), QMetaMethod::Private);
QVERIFY(!(m.attributes() & QMetaMethod::Scriptable));
QVERIFY(!(m.attributes() & QMetaMethod::Compatibility));
m = mobj->method(mobj->indexOfMethod("sinvoke3()"));
QVERIFY(m.methodSignature() == "sinvoke3()");
QCOMPARE(m.methodType(), QMetaMethod::Method);
QCOMPARE(m.access(), QMetaMethod::Private);
QVERIFY((m.attributes() & QMetaMethod::Scriptable));
QVERIFY(!(m.attributes() & QMetaMethod::Compatibility));
m = mobj->method(mobj->indexOfMethod("signal5()"));
QVERIFY(m.methodSignature() == "signal5()");
QCOMPARE(m.methodType(), QMetaMethod::Signal);
QCOMPARE(m.access(), QMetaMethod::Public);
QVERIFY(!(m.attributes() & QMetaMethod::Scriptable));
QVERIFY((m.attributes() & QMetaMethod::Compatibility));
m = mobj->method(mobj->indexOfMethod("aPublicSlot()"));
QVERIFY(m.methodSignature() == "aPublicSlot()");
QCOMPARE(m.methodType(), QMetaMethod::Slot);
QCOMPARE(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::senderTest()
{
{
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);
}
#ifndef QT_NO_USERDATA
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 = QRandomGenerator::global()->bounded(USER_DATA_COUNT);
int p2 = QRandomGenerator::global()->bounded(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 != nullptr);
QCOMPARE(data->id, id);
}
}
#endif // QT_NO_USERDATA
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 >) {}
};
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 &))));
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());
QCOMPARE(int(prop.type()), QMetaType::type("float"));
QVERIFY(!prop.write(&obj, QVariant("Hello")));
QVERIFY(prop.write(&obj, QVariant::fromValue(128.0f)));
QVariant v = prop.read(&obj);
QCOMPARE(v.userType(), int(QMetaType::Float));
QCOMPARE(qvariant_cast<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());
QCOMPARE(int(prop.type()), QMetaType::type("qreal"));
QVERIFY(!prop.write(&obj, QVariant("Hello")));
QVERIFY(prop.write(&obj, QVariant::fromValue(128.0f)));
QVariant v = prop.read(&obj);
QCOMPARE(v.userType(), qMetaTypeId<qreal>());
QCOMPARE(qvariant_cast<qreal>(v), 128.0);
QVERIFY(prop.write(&obj, QVariant::fromValue(double(127))));
v = prop.read(&obj);
QCOMPARE(v.userType(), qMetaTypeId<qreal>());
QCOMPARE(qvariant_cast<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());
// set a non-dynamic property
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());
// set a dynamic property
QVERIFY(!obj.setProperty("myuserproperty", "Hello"));
QCOMPARE(obj.changedDynamicProperties.count(), 1);
QCOMPARE(obj.changedDynamicProperties.first(), QByteArray("myuserproperty"));
//check if there is no redundant DynamicPropertyChange events
QVERIFY(!obj.setProperty("myuserproperty", "Hello"));
QCOMPARE(obj.changedDynamicProperties.count(), 1);
QCOMPARE(obj.property("myuserproperty").type(), QVariant::String);
QCOMPARE(obj.property("myuserproperty").toString(), QString("Hello"));
QCOMPARE(obj.dynamicPropertyNames().count(), 1);
QCOMPARE(obj.dynamicPropertyNames().first(), QByteArray("myuserproperty"));
// change type of the dynamic property
obj.changedDynamicProperties.clear();
QVERIFY(!obj.setProperty("myuserproperty", QByteArray("Hello")));
QCOMPARE(obj.changedDynamicProperties.count(), 1);
QCOMPARE(obj.changedDynamicProperties.first(), QByteArray("myuserproperty"));
QCOMPARE(obj.property("myuserproperty").type(), QVariant::ByteArray);
QCOMPARE(obj.property("myuserproperty").toString(), QByteArray("Hello"));
// unset the property
obj.changedDynamicProperties.clear();
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 !QT_CONFIG(process)
QSKIP("No qprocess support", SkipAll);
#else
QProcess proc;
// signalbug helper app should always be next to this test binary
const QString path = QStringLiteral("signalbug_helper");
proc.start(path);
QVERIFY2(proc.waitForStarted(), qPrintable(QString::fromLatin1("Cannot start '%1': %2").arg(path, proc.errorString())));
QVERIFY(proc.waitForFinished());
QCOMPARE(proc.exitStatus(), QProcess::NormalExit);
QCOMPARE(proc.exitCode(), 0);
#endif
}
void tst_QObject::signalBlocking()
{
SenderObject sender;
ReceiverObject receiver;
receiver.connect(&sender, SIGNAL(signal1()), SLOT(slot1()));
sender.emitSignal1();
QVERIFY(receiver.called(1));
receiver.reset();
sender.blockSignals(true);
sender.emitSignal1();
QVERIFY(!receiver.called(1));
receiver.reset();
sender.blockSignals(false);
sender.emitSignal1();
QVERIFY(receiver.called(1));
receiver.reset();
}
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));
connect(&sender, &SenderObject::signal2, &receiver, &ReceiverObject::slot2, Qt::BlockingQueuedConnection);
sender.emitSignal2();
QVERIFY(receiver.called(2));
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::childEvents()
{
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 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
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, &QObject::destroyed, &b, &QObject::deleteLater);
QTest::ignoreMessage(QtDebugMsg, "OBJECT QObject::unnamed");
QTest::ignoreMessage(QtDebugMsg, " SIGNALS OUT");
QTest::ignoreMessage(QtDebugMsg, " signal: destroyed(QObject*)");
QTest::ignoreMessage(QtDebugMsg, " <functor or function pointer>");
QTest::ignoreMessage(QtDebugMsg, " SIGNALS IN");
QTest::ignoreMessage(QtDebugMsg, " <None>");
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;
ReceiverObject r1;
ReceiverObject r2;
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);
}
void tst_QObject::uniqConnectionPtr()
{
SenderObject s;
ReceiverObject r1;
ReceiverObject r2;
r1.reset();
r2.reset();
ReceiverObject::sequence = 0;
QVERIFY(connect(&s, &SenderObject::signal1, &r1, &ReceiverObject::slot1 ,
Qt::UniqueConnection));
QVERIFY(connect(&s, &SenderObject::signal1, &r2, &ReceiverObject::slot1 ,
Qt::UniqueConnection));
QVERIFY(connect(&s, &SenderObject::signal1, &r1, &ReceiverObject::slot3 ,
Qt::UniqueConnection));
QVERIFY(connect(&s, &SenderObject::signal3, &r1, &ReceiverObject::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, &SenderObject::signal4, &r1, &ReceiverObject::slot4 ,
Qt::UniqueConnection));
QVERIFY( connect(&s, &SenderObject::signal4, &r2, &ReceiverObject::slot4 ,
Qt::UniqueConnection));
QVERIFY(!connect(&s, &SenderObject::signal4, &r2, &ReceiverObject::slot4 ,
Qt::UniqueConnection));
QVERIFY( connect(&s, &SenderObject::signal1, &r2, &ReceiverObject::slot4 ,
Qt::UniqueConnection));
QVERIFY(!connect(&s, &SenderObject::signal4, &r1, &ReceiverObject::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, &SenderObject::signal4, &r1, &ReceiverObject::slot4);
s.emitSignal4();
QCOMPARE(r1.count_slot4, 2);
QCOMPARE(r2.count_slot4, 1);
QCOMPARE(r1.sequence_slot4, 3);
QCOMPARE(r2.sequence_slot4, 2);
}
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 a regression test for an old bug that used to deadlock
// when the QObject from the event was destroyed.
struct MyEvent : public QEvent
{
MyEvent() : QEvent(QEvent::User) { }
QObject obj;
};
QObject o;
QCoreApplication::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 = nullptr);
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;
const QMetaObject *meta = o.metaObject();
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()")));
#endif
QVERIFY(!o.isSignalConnected(meta->method(meta->indexOfSignal("destroyed()"))));
QVERIFY(!o.isSignalConnected(meta->method(meta->indexOfSignal("sig00()"))));
QVERIFY(!o.isSignalConnected(meta->method(meta->indexOfSignal("sig05()"))));
QVERIFY(!o.isSignalConnected(meta->method(meta->indexOfSignal("sig15()"))));
QVERIFY(!o.isSignalConnected(meta->method(meta->indexOfSignal("sig29()"))));
QVERIFY(!o.isSignalConnected(meta->method(meta->indexOfSignal("sig60()"))));
QVERIFY(!o.isSignalConnected(meta->method(meta->indexOfSignal("sig69()"))));
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
QVERIFY(!o.isSignalConnected(meta->method(meta->indexOfSignal("destroyed()"))));
QVERIFY(!o.isSignalConnected(meta->method(meta->indexOfSignal("destroyed(QObject*)"))));
QVERIFY(!o.isSignalConnected(meta->method(meta->indexOfSignal("sig05()"))));
QVERIFY(!o.isSignalConnected(meta->method(meta->indexOfSignal("sig15()"))));
QVERIFY(!o.isSignalConnected(meta->method(meta->indexOfSignal("sig29()"))));
QVERIFY(o.isSignalConnected(meta->method(meta->indexOfSignal("sig00()"))));
QVERIFY(o.isSignalConnected(meta->method(meta->indexOfSignal("sig03()"))));
QVERIFY(o.isSignalConnected(meta->method(meta->indexOfSignal("sig34()"))));
QVERIFY(o.isSignalConnected(meta->method(meta->indexOfSignal("sig69()"))));
QVERIFY(!o.isSignalConnected(meta->method(meta->indexOfSignal("sig18()"))));
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
QVERIFY(o.isSignalConnected(meta->method(meta->indexOfSignal("sig18()"))));
QVERIFY(o.isSignalConnected(meta->method(meta->indexOfSignal("sig62()"))));
QVERIFY(o.isSignalConnected(meta->method(meta->indexOfSignal("sig28()"))));
QVERIFY(o.isSignalConnected(meta->method(meta->indexOfSignal("sig69()"))));
QVERIFY(!o.isSignalConnected(meta->method(meta->indexOfSignal("sig27()"))));
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()")));
#endif
emit o.sig00();
QCOMPARE(o.rec, 1);
emit o.sig69();
QCOMPARE(o.rec, 2);
emit o.sig36();
QCOMPARE(o.rec, 2);
QObject::connect(&o, &QObject::destroyed, qt_noop);
QVERIFY(o.isSignalConnected(meta->method(meta->indexOfSignal("destroyed()"))));
QVERIFY(o.isSignalConnected(meta->method(meta->indexOfSignal("destroyed(QObject*)"))));
QVERIFY(!o.isSignalConnected(QMetaMethod()));
}
void tst_QObject::isSignalConnectedAfterDisconnection()
{
ManySignals o;
const QMetaObject *meta = o.metaObject();
const QMetaMethod sig00 = meta->method(meta->indexOfSignal("sig00()"));
QVERIFY(!o.isSignalConnected(sig00));
QObject::connect(&o, &ManySignals::sig00, qt_noop);
QVERIFY(o.isSignalConnected(sig00));
QVERIFY(QObject::disconnect(&o, &ManySignals::sig00, 0, 0));
QVERIFY(!o.isSignalConnected(sig00));
const QMetaMethod sig69 = meta->method(meta->indexOfSignal("sig69()"));
QVERIFY(!o.isSignalConnected(sig69));
QObject::connect(&o, &ManySignals::sig69, qt_noop);
QVERIFY(o.isSignalConnected(sig69));
QVERIFY(QObject::disconnect(&o, &ManySignals::sig69, 0, 0));
QVERIFY(!o.isSignalConnected(sig69));
{
ManySignals o2;
QObject::connect(&o, &ManySignals::sig00, &o2, &ManySignals::sig00);
QVERIFY(o.isSignalConnected(sig00));
// o2 is destructed
}
QVERIFY(!o.isSignalConnected(sig00));
const QMetaMethod sig01 = meta->method(meta->indexOfSignal("sig01()"));
QObject::connect(&o, &ManySignals::sig00, qt_noop);
QObject::connect(&o, &ManySignals::sig01, qt_noop);
QObject::connect(&o, &ManySignals::sig69, qt_noop);
QVERIFY(o.isSignalConnected(sig00));
QVERIFY(o.isSignalConnected(sig01));
QVERIFY(o.isSignalConnected(sig69));
QVERIFY(QObject::disconnect(&o, &ManySignals::sig69, 0, 0));
QVERIFY(o.isSignalConnected(sig00));
QVERIFY(o.isSignalConnected(sig01));
QVERIFY(!o.isSignalConnected(sig69));
QVERIFY(QObject::disconnect(&o, &ManySignals::sig00, 0, 0));
QVERIFY(!o.isSignalConnected(sig00));
QVERIFY(o.isSignalConnected(sig01));
QVERIFY(!o.isSignalConnected(sig69));
QObject::connect(&o, &ManySignals::sig69, qt_noop);
QVERIFY(!o.isSignalConnected(sig00));
QVERIFY(o.isSignalConnected(sig01));
QVERIFY(o.isSignalConnected(sig69));
QVERIFY(QObject::disconnect(&o, &ManySignals::sig01, 0, 0));
QVERIFY(!o.isSignalConnected(sig00));
QVERIFY(!o.isSignalConnected(sig01));
QVERIFY(o.isSignalConnected(sig69));
}
void tst_QObject::qMetaObjectConnect()
{
ReceiverObject r1;
ReceiverObject r2;
int slot1Index, slot2Index, slot3Index;
{
SenderObject s;
r1.reset();
r2.reset();
ReceiverObject::sequence = 0;
int signal1Index = s.metaObject()->indexOfSignal("signal1()");
int signal3Index = s.metaObject()->indexOfSignal("signal3()");
slot1Index = r1.metaObject()->indexOfSlot("slot1()");
slot2Index = r1.metaObject()->indexOfSlot("slot2()");
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);
}
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);
}
void tst_QObject::qMetaObjectDisconnectOne()
{
SenderObject s;
ReceiverObject r1;
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(!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(!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);
}
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);
#ifndef QT_NO_DEBUG
QTest::ignoreMessage(QtWarningMsg, "QMetaObject::indexOfSignal: signal aPublicSlot() from SenderObject redefined in ConfusingObject");
#endif
QVERIFY(connect(&c2, SIGNAL(aPublicSlot()), &c1, SLOT(signal1())));
c2.aPublicSlot();
QCOMPARE(c2.aPublicSlotCalled, 0);
QCOMPARE(c1.aPublicSlotCalled, 0);
QCOMPARE(c1.s, 3);
#ifndef QT_NO_DEBUG
QTest::ignoreMessage(QtWarningMsg, "QMetaObject::indexOfSignal: signal aPublicSlot() from SenderObject redefined in ConfusingObject");
#endif
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;
ReceiverObject r1;
ReceiverObject r2;
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 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));
}
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,"QObject::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";
}
};
static void processEvents()
{
qApp->processEvents();
}
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
}
{
BaseDestroyed d;
connect(&d, &QObject::destroyed, processEvents);
QMetaObject::invokeMethod(&d, "slotUseList", Qt::QueuedConnection);
//the destructor will call processEvents, that should not call the slotUseList
}
}
void tst_QObject::pointerConnect()
{
SenderObject s;
ReceiverObject r1;
ReceiverObject r2;
r1.reset();
r2.reset();
ReceiverObject::sequence = 0;
QTimer timer;
QVERIFY(connect(&s, &SenderObject::signal1 , &r1, &ReceiverObject::slot1));
QVERIFY(connect(&s, &SenderObject::signal1 , &r2, &ReceiverObject::slot1));
QVERIFY(connect(&s, &SenderObject::signal1 , &r1, &ReceiverObject::slot3));
QVERIFY(connect(&s, &SenderObject::signal3 , &r1, &ReceiverObject::slot3));
QVERIFY2(connect(&timer, &QTimer::timeout, &r1, &ReceiverObject::deleteLater),
"Signal connection failed most likely due to failing comparison of pointers to member "
"functions caused by problems with -reduce-relocations on this platform.");
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, &SenderObject::signal4, &r1, &ReceiverObject::slot4));
QVERIFY(connect(&s, &SenderObject::signal4, &r2, &ReceiverObject::slot4));
QVERIFY(connect(&s, &SenderObject::signal1, &r2, &ReceiverObject::slot4));
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, &SenderObject::signal4 , &r1, &ReceiverObject::slot4);
s.emitSignal4();
QCOMPARE(r1.count_slot4, 2);
QCOMPARE(r2.count_slot4, 1);
QCOMPARE(r1.sequence_slot4, 3);
QCOMPARE(r2.sequence_slot4, 2);
QMetaObject::Connection con;
QVERIFY(!con);
QVERIFY(!QObject::disconnect(con));
//connect a slot to a signal (== error)
QTest::ignoreMessage(QtWarningMsg, "QObject::connect: signal not found in ReceiverObject");
con = connect(&r1, &ReceiverObject::slot4 , &s, &SenderObject::signal4);
QVERIFY(!con);
QVERIFY(!QObject::disconnect(con));
}
void tst_QObject::pointerDisconnect()
{
SenderObject s;
ReceiverObject r1;
ReceiverObject r2;
connect(&s, &SenderObject::signal1, &r1, &ReceiverObject::slot1);
connect(&s, &SenderObject::signal2, &r1, &ReceiverObject::slot2);
connect(&s, &SenderObject::signal3, &r1, &ReceiverObject::slot3);
connect(&s, &SenderObject::signal4, &r1, &ReceiverObject::slot4);
s.emitSignal1();
s.emitSignal2();
s.emitSignal3();
s.emitSignal4();
QVERIFY(r1.called(1));
QVERIFY(r1.called(2));
QVERIFY(r1.called(3));
QVERIFY(r1.called(4));
r1.reset();
// usual disconnect with all parameters given
bool ret = QObject::disconnect(&s, &SenderObject::signal1, &r1, &ReceiverObject::slot1);
s.emitSignal1();
QVERIFY(!r1.called(1));
r1.reset();
QVERIFY(ret);
ret = QObject::disconnect(&s, &SenderObject::signal1, &r1, &ReceiverObject::slot1);
QVERIFY(!ret);
// disconnect all signals from s from all slots from r1
QObject::disconnect(&s, 0, &r1, 0);
s.emitSignal2();
s.emitSignal3();
s.emitSignal4();
QVERIFY(!r1.called(2));
QVERIFY(!r1.called(3));
QVERIFY(!r1.called(4));
r1.reset();
connect(&s, &SenderObject::signal1, &r1, &ReceiverObject::slot1);
connect(&s, &SenderObject::signal1, &r1, &ReceiverObject::slot2);
connect(&s, &SenderObject::signal1, &r1, &ReceiverObject::slot3);
connect(&s, &SenderObject::signal2, &r1, &ReceiverObject::slot4);
// disconnect s's signal1() from all slots of r1
QObject::disconnect(&s, &SenderObject::signal1, &r1, 0);
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, &SenderObject::signal1, &r1, &ReceiverObject::slot1);
connect(&s, &SenderObject::signal1, &r2, &ReceiverObject::slot1);
connect(&s, &SenderObject::signal2, &r1, &ReceiverObject::slot2);
connect(&s, &SenderObject::signal2, &r2, &ReceiverObject::slot2);
connect(&s, &SenderObject::signal3, &r1, &ReceiverObject::slot3);
connect(&s, &SenderObject::signal3, &r2, &ReceiverObject::slot3);
// disconnect signal1() from all receivers
QObject::disconnect(&s, &SenderObject::signal1, 0, 0);
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);
QVERIFY(!r1.called(2));
QVERIFY(!r2.called(2));
QVERIFY(!r1.called(2));
QVERIFY(!r2.called(2));
}
void tst_QObject::emitInDefinedOrderPointer()
{
SenderObject sender;
ReceiverObject receiver1, receiver2, receiver3, receiver4;
QMetaObject::Connection h0 = connect(&sender, &SenderObject::signal1, &receiver1, &SequenceObject::slot1);
QMetaObject::Connection h1 = connect(&sender, &SenderObject::signal1, &receiver2, &SequenceObject::slot1);
QVERIFY(h0);
QVERIFY(h1);
connect(&sender, &SenderObject::signal1, &receiver3, &SequenceObject::slot1);
connect(&sender, &SenderObject::signal1, &receiver4, &SequenceObject::slot1);
connect(&sender, &SenderObject::signal1, &receiver1, &SequenceObject::slot2);
connect(&sender, &SenderObject::signal1, &receiver2, &SequenceObject::slot2);
connect(&sender, &SenderObject::signal1, &receiver3, &SequenceObject::slot2);
connect(&sender, &SenderObject::signal1, &receiver4, &SequenceObject::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(h1);
h1 = connect(&sender, &SenderObject::signal1, &receiver2, &SequenceObject::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(h0);
h0 = connect(&sender, &SenderObject::signal1, &receiver1, &SequenceObject::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);
QVERIFY(QObject::disconnect(h0));
QVERIFY(!QObject::disconnect(h0));
}
void tst_QObject::customTypesPointer()
{
CustomType t0;
CustomType t1(1, 2, 3);
CustomType t2(2, 3, 4);
{
QCustomTypeChecker checker;
QCOMPARE(instanceCount, 4);
connect(&checker, &QCustomTypeChecker::signal1, &checker, &QCustomTypeChecker::slot1,
Qt::DirectConnection);
QCOMPARE(checker.received.value(), 0);
checker.doEmit(t1);
QCOMPARE(checker.received.value(), t1.value());
checker.received = t0;
checker.disconnect();
int idx = qRegisterMetaType<CustomType>("CustomType");
QCOMPARE(QMetaType::type("CustomType"), idx);
connect(&checker, &QCustomTypeChecker::signal1, &checker, &QCustomTypeChecker::slot1,
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));
// Test auto registered type (QList<CustomType>)
QList<CustomType> list;
QCOMPARE(instanceCount, 4);
list.append(t1);
QCOMPARE(instanceCount, 5);
QVERIFY(connect(&checker, &QCustomTypeChecker::signal2,
&checker, &QCustomTypeChecker::slot2, Qt::QueuedConnection));
emit checker.signal2(list);
QCOMPARE(instanceCount, 5); //because the list is implicitly shared.
list.clear();
QCOMPARE(instanceCount, 5);
QCoreApplication::processEvents();
QCOMPARE(checker.received.value(), t1.value());
QCOMPARE(instanceCount, 4);
}
QCOMPARE(instanceCount, 3);
}
void tst_QObject::connectCxx0x()
{
SenderObject s;
ReceiverObject r1;
QObject::connect(&s, &SenderObject::signal1, &r1, &ReceiverObject::slot1);
QObject::connect(&s, &SenderObject::signal3, &r1, &ReceiverObject::slot2);
QObject::connect(&s, &SenderObject::signal3, &r1, &ReceiverObject::slot2);
QObject::connect(&s, &SenderObject::signal3, &r1, &ReceiverObject::slot2);
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);
// connect signal to signal
QObject::connect(&s, &SenderObject::signal2, &s, &SenderObject::signal1);
r1.reset();
s.emitSignal2();
QCOMPARE(r1.count_slot1, 1);
}
int receivedCount;
void receiverFunction() { ++receivedCount; }
void tst_QObject::connectToStaticCxx0x()
{
SenderObject *s = new SenderObject;
void (*receiver)() = receiverFunction;
QObject::connect(s, &SenderObject::signal1, receiver);
receivedCount = 0;
s->emitSignal1();
QCOMPARE(receivedCount, 1);
QObject::connect(s, &SenderObject::signal1, receiver);
receivedCount = 0;
s->emitSignal1();
QCOMPARE(receivedCount, 2);
delete s;
}
class LotsOfSignalsAndSlots: public QObject
{
Q_OBJECT
typedef void (*fptr)();
public slots:
void slot_v() {}
void slot_v_noexcept() noexcept {}
void slot_vi(int) {}
void slot_vi_noexcept() noexcept {}
void slot_vii(int, int) {}
void slot_viii(int, int, int) {}
int slot_i() { return 0; }
int slot_i_noexcept() noexcept { return 0; }
int slot_ii(int) { return 0; }
int slot_iii(int, int) { return 0; }
int slot_iiii(int, int, int) { return 0; }
void slot_vRi(int &) {}
void slot_vs(short) {}
void slot_vRs(short&) {}
/* #ifdef Q_COMPILER_RVALUE_REFS
void slot_vOi(int &&) {}
void slot_vOs(short &&) {}
#endif*/
void slot_vPFvvE(fptr) {}
void const_slot_v() const {};
void const_slot_v_noexcept() const noexcept {}
void const_slot_vi(int) const {};
void const_slot_vi_noexcept(int) const noexcept {}
static void static_slot_v() {}
static void static_slot_v_noexcept() noexcept {}
static void static_slot_vi(int) {}
static void static_slot_vi_noexcept(int) noexcept {}
static void static_slot_vii(int, int) {}
static void static_slot_viii(int, int, int) {}
static int static_slot_i() { return 0; }
static int static_slot_i_noexcept() noexcept { return 0; }
static int static_slot_ii(int) { return 0; }
static int static_slot_iii(int, int) { return 0; }
static int static_slot_iiii(int, int, int) { return 0; }
static void static_slot_vRi(int &) {}
static void static_slot_vs(short) {}
static void static_slot_vRs(short&) {}
/* #if defined(Q_COMPILER_RVALUE_REFS) || defined(QT_ENABLE_CXX0X)
static void static_slot_vOi(int &&) {}
static void static_slot_vOs(short &&) {}
#endif*/
static void static_slot_vPFvvE(fptr) {}
void slot_vcRQObject(const QObject &) {}
void slot_vRQObject(QObject &) {}
signals:
void signal_v();
void signal_vi(int);
void signal_vii(int, int);
void signal_viii(int, int, int);
void signal_vRi(int &);
void signal_vs(short);
void signal_vRs(short &);
/* #if defined(Q_COMPILER_RVALUE_REFS) || defined(QT_ENABLE_CXX0X)
void signal_vOi(int &&);
void signal_vOs(short &&);
#endif*/
void signal_vPFvvE(fptr);
void const_signal_v() const;
void const_signal_vi(int) const;
void signal_vcRQObject(const QObject &);
void signal_vRQObject(QObject &);
void signal(short&, short, long long, short);
void otherSignal(const char *);
};
void tst_QObject::connectCxx0xTypeMatching()
{
// this is just about connecting the signals to the slots
// if this fails, this will be a compiler failure
typedef LotsOfSignalsAndSlots Foo;
Foo obj;
// member connects
QObject::connect(&obj, &Foo::signal_v, &obj, &Foo::slot_v);
QObject::connect(&obj, &Foo::signal_v, &obj, &Foo::slot_i);
QObject::connect(&obj, &Foo::signal_vi, &obj, &Foo::slot_v);
QObject::connect(&obj, &Foo::signal_vi, &obj, &Foo::slot_i);
QObject::connect(&obj, &Foo::signal_vi, &obj, &Foo::slot_vi);
QObject::connect(&obj, &Foo::signal_vi, &obj, &Foo::slot_ii);
QObject::connect(&obj, &Foo::signal_vii, &obj, &Foo::slot_v);
QObject::connect(&obj, &Foo::signal_vii, &obj, &Foo::slot_i);
QObject::connect(&obj, &Foo::signal_vii, &obj, &Foo::slot_vi);
QObject::connect(&obj, &Foo::signal_vii, &obj, &Foo::slot_ii);
QObject::connect(&obj, &Foo::signal_vii, &obj, &Foo::slot_vii);
QObject::connect(&obj, &Foo::signal_vii, &obj, &Foo::slot_iii);
QObject::connect(&obj, &Foo::signal_viii, &obj, &Foo::slot_v);
QObject::connect(&obj, &Foo::signal_viii, &obj, &Foo::slot_i);
QObject::connect(&obj, &Foo::signal_viii, &obj, &Foo::slot_vi);
QObject::connect(&obj, &Foo::signal_viii, &obj, &Foo::slot_ii);
QObject::connect(&obj, &Foo::signal_viii, &obj, &Foo::slot_vii);
QObject::connect(&obj, &Foo::signal_viii, &obj, &Foo::slot_iii);
QObject::connect(&obj, &Foo::signal_viii, &obj, &Foo::slot_viii);
QObject::connect(&obj, &Foo::signal_viii, &obj, &Foo::slot_iiii);
QObject::connect(&obj, &Foo::signal_vi, &obj, &Foo::slot_vi); // repeated from above
QObject::connect(&obj, &Foo::signal_vRi, &obj, &Foo::slot_vi);
QObject::connect(&obj, &Foo::signal_vRi, &obj, &Foo::slot_vRi);
/*#if defined(Q_COMPILER_RVALUE_REFS) || defined(QT_ENABLE_CXX0X)
QObject::connect(&obj, &Foo::signal_vOi, &obj, &Foo::slot_vi);
QObject::connect(&obj, &Foo::signal_vi, &obj, &Foo::slot_vOi);
QObject::connect(&obj, &Foo::signal_vRi, &obj, &Foo::slot_vOi);
QObject::connect(&obj, &Foo::signal_vOi, &obj, &Foo::slot_vOi);
#endif*/
// these are not supposed to compile:
//QObject::connect(&obj, &Foo::signal_vi, &obj, &Foo::slot_vRi);
//QObject::connect(&obj, &Foo::signal_vOi, &obj, &Foo::slot_vRi);
QObject::connect(&obj, &Foo::signal_vs, &obj, &Foo::slot_vi);
QObject::connect(&obj, &Foo::signal_vRs, &obj, &Foo::slot_vi);
/*#if defined(Q_COMPILER_RVALUE_REFS) || defined(QT_ENABLE_CXX0X)
QObject::connect(&obj, &Foo::signal_vOs, &obj, &Foo::slot_vi);
QObject::connect(&obj, &Foo::signal_vRs, &obj, &Foo::slot_vOi);
QObject::connect(&obj, &Foo::signal_vOs, &obj, &Foo::slot_vOi);
// these are not supposed to compile:
//QObject::connect(&obj, &Foo::signal_vOs, &obj, &Foo::slot_vRi);
//QObject::connect(&obj, &Foo::signal_vRs, &obj, &Foo::slot_vRi);
#endif*/
QObject::connect(&obj, &Foo::signal_vPFvvE, &obj, &Foo::slot_v);
QObject::connect(&obj, &Foo::signal_vPFvvE, &obj, &Foo::slot_i);
QObject::connect(&obj, &Foo::signal_vPFvvE, &obj, &Foo::slot_vPFvvE);
QObject::connect(&obj, &Foo::signal_v, &Foo::static_slot_v);
QObject::connect(&obj, &Foo::signal_v, &Foo::static_slot_i);
QObject::connect(&obj, &Foo::signal_vi, &Foo::static_slot_v);
QObject::connect(&obj, &Foo::signal_vi, &Foo::static_slot_i);
QObject::connect(&obj, &Foo::signal_vi, &Foo::static_slot_vi);
QObject::connect(&obj, &Foo::signal_vi, &Foo::static_slot_ii);
QObject::connect(&obj, &Foo::signal_vii, &Foo::static_slot_v);
QObject::connect(&obj, &Foo::signal_vii, &Foo::static_slot_i);
QObject::connect(&obj, &Foo::signal_vii, &Foo::static_slot_vi);
QObject::connect(&obj, &Foo::signal_vii, &Foo::static_slot_ii);
QObject::connect(&obj, &Foo::signal_vii, &Foo::static_slot_vii);
QObject::connect(&obj, &Foo::signal_vii, &Foo::static_slot_iii);
QObject::connect(&obj, &Foo::signal_viii, &Foo::static_slot_v);
QObject::connect(&obj, &Foo::signal_viii, &Foo::static_slot_i);
QObject::connect(&obj, &Foo::signal_viii, &Foo::static_slot_vi);
QObject::connect(&obj, &Foo::signal_viii, &Foo::static_slot_ii);
QObject::connect(&obj, &Foo::signal_viii, &Foo::static_slot_vii);
QObject::connect(&obj, &Foo::signal_viii, &Foo::static_slot_iii);
QObject::connect(&obj, &Foo::signal_viii, &Foo::static_slot_viii);
QObject::connect(&obj, &Foo::signal_viii, &Foo::static_slot_iiii);
/*#if defined(Q_COMPILER_RVALUE_REFS) && defined(QT_ENABLE_CXX0X)
QObject::connect(&obj, &Foo::signal_vi, &Foo::static_slot_vOi);
QObject::connect(&obj, &Foo::signal_vRi, &Foo::static_slot_vi);
QObject::connect(&obj, &Foo::signal_vRi, &Foo::static_slot_vRi);
QObject::connect(&obj, &Foo::signal_vRi, &Foo::static_slot_vOi);
QObject::connect(&obj, &Foo::signal_vOi, &Foo::static_slot_vi);
QObject::connect(&obj, &Foo::signal_vOi, &Foo::static_slot_vOi);
//QObject::connect(&obj, &Foo::signal_vi, &Foo::static_slot_vRi);
//QObject::connect(&obj, &Foo::signal_vOi, &Foo::static_slot_vRi);
#endif*/
QObject::connect(&obj, &Foo::signal_vs, &Foo::static_slot_vi);
QObject::connect(&obj, &Foo::signal_vRs, &Foo::static_slot_vi);
/*#if defined(Q_COMPILER_RVALUE_REFS) && defined(QT_ENABLE_CXX0X)
QObject::connect(&obj, &Foo::signal_vOs, &Foo::static_slot_vi);
QObject::connect(&obj, &Foo::signal_vRs, &Foo::static_slot_vOi);
QObject::connect(&obj, &Foo::signal_vOs, &Foo::static_slot_vOi);
//QObject::connect(&obj, &Foo::signal_vOs, &Foo::static_slot_vRi);
//QObject::connect(&obj, &Foo::signal_vRs, &Foo::static_slot_vRi);
#endif*/
QObject::connect(&obj, &Foo::signal_vPFvvE, &Foo::static_slot_v);
QObject::connect(&obj, &Foo::signal_vPFvvE, &Foo::static_slot_i);
QObject::connect(&obj, &Foo::signal_vPFvvE, &Foo::static_slot_vPFvvE);
QVERIFY(QObject::connect(&obj, &Foo::const_signal_v, &obj, &Foo::const_slot_v));
QVERIFY(QObject::connect(&obj, &Foo::const_signal_vi, &obj, &Foo::const_slot_v));
QVERIFY(QObject::connect(&obj, &Foo::const_signal_vi, &obj, &Foo::slot_vi));
QVERIFY(QObject::connect(&obj, &Foo::signal_vi, &obj, &Foo::const_slot_vi));
QVERIFY(QObject::connect(&obj, &Foo::signal_vi, &obj, &Foo::const_slot_v));
QVERIFY(QObject::connect(&obj, &Foo::signal_vcRQObject, &obj, &Foo::slot_vcRQObject));
QVERIFY(QObject::connect(&obj, &Foo::signal_vRQObject, &obj, &Foo::slot_vRQObject));
QVERIFY(QObject::connect(&obj, &Foo::signal_vRQObject, &obj, &Foo::slot_vcRQObject));
// QVERIFY(QObject::connect(&obj, &Foo::signal_vcRQObject, &obj, &Foo::slot_vRQObject)); // Should be an error (const& -> &)
QVERIFY(QObject::connect(&obj, &Foo::signal_vRi, &obj, &Foo::slot_vs));
}
void receiverFunction_noexcept() noexcept {}
struct Functor_noexcept { void operator()() noexcept {} };
void tst_QObject::connectCxx17Noexcept()
{
// this is about connecting signals to slots with the noexcept qualifier
// as semantics changed due to http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0012r1.html
typedef LotsOfSignalsAndSlots Foo;
Foo obj;
QObject::connect(&obj, &Foo::signal_v, &obj, &Foo::slot_v_noexcept);
QObject::connect(&obj, &Foo::signal_v, &obj, &Foo::slot_i_noexcept);
QObject::connect(&obj, &Foo::signal_v, &obj, &Foo::slot_vi_noexcept);
QObject::connect(&obj, &Foo::signal_vii, &Foo::static_slot_v_noexcept);
QObject::connect(&obj, &Foo::signal_vii, &Foo::static_slot_i_noexcept);
QObject::connect(&obj, &Foo::signal_vii, &Foo::static_slot_vi_noexcept);
QVERIFY(QObject::connect(&obj, &Foo::signal_vi, &obj, &Foo::const_slot_vi_noexcept));
QVERIFY(QObject::connect(&obj, &Foo::signal_vi, &obj, &Foo::const_slot_v_noexcept));
QObject::connect(&obj, &Foo::signal_v, receiverFunction_noexcept);
Functor_noexcept fn;
QObject::connect(&obj, &Foo::signal_v, fn);
}
class StringVariant : public QObject
{ Q_OBJECT
signals:
void stringSignal(const QString &str);
public slots:
void variantSlot(const QVariant &v) { var = v; }
public:
QVariant var;
friend class tst_QObject;
};
struct Functor {
QVariant *var;
void operator() (const QVariant &v) {
*var = v;
}
};
void tst_QObject::connectConvert()
{
StringVariant obj;
QVERIFY(connect(&obj, &StringVariant::stringSignal, &obj, &StringVariant::variantSlot));
QString s = QString::fromLatin1("Hello World");
emit obj.stringSignal(s);
QCOMPARE(obj.var.toString(), s);
QVERIFY(obj.var.toString().isSharedWith(s));
QVariant var;
Functor f;
f.var = &var;
QVERIFY(connect(&obj, &StringVariant::stringSignal, f));
s = QString::fromLatin1("GoodBye");
emit obj.stringSignal(s);
QCOMPARE(obj.var.toString(), s);
QVERIFY(obj.var.toString().isSharedWith(s));
QCOMPARE(var, obj.var);
}
class ConnectWithReferenceObject : public QObject {
Q_OBJECT
friend class tst_QObject;
signals:
void boolRef(bool &, bool);
void stringRef(QString &, const QString &);
void boolPtr(bool *, bool);
void stringPtr(QString *, const QString &);
public slots:
void boolRefSlot(bool &b1, bool b2) { b1 = b2; }
void stringRefSlot(QString &s1, const QString &s2) { s1 = s2; }
void boolPtrSlot(bool *b1, bool b2) { *b1 = b2; }
void stringPtrSlot(QString *s1, const QString &s2) { *s1 = s2; }
void stringSlot1(QString s) { last = s; }
void stringSlot2(const QString &s) { last = s; }
void stringSlot3(QString &s) { last = s; }
public:
QString last;
};
void tst_QObject::connectWithReference()
{
ConnectWithReferenceObject o;
bool b1 = true;
QString s1 = QString::fromLatin1("str1");
const QString s2 = QString::fromLatin1("str2");
const QString s3 = QString::fromLatin1("str3");
o.boolRef(b1, false);
o.stringRef(s1, s2);
QCOMPARE(b1, true);
QCOMPARE(s1, QString::fromLatin1("str1"));
o.boolPtr(&b1, false);
o.stringPtr(&s1, s2);
QCOMPARE(b1, true);
QCOMPARE(s1, QString::fromLatin1("str1"));
QVERIFY(connect(&o, &ConnectWithReferenceObject::boolRef, &o, &ConnectWithReferenceObject::boolRefSlot));
QVERIFY(connect(&o, &ConnectWithReferenceObject::stringRef, &o, &ConnectWithReferenceObject::stringRefSlot));
QVERIFY(connect(&o, &ConnectWithReferenceObject::boolPtr, &o, &ConnectWithReferenceObject::boolPtrSlot));
QVERIFY(connect(&o, &ConnectWithReferenceObject::stringPtr, &o, &ConnectWithReferenceObject::stringPtrSlot));
o.boolRef(b1, false);
o.stringRef(s1, s2);
QCOMPARE(b1, false);
QCOMPARE(s1, QString::fromLatin1("str2"));
o.boolPtr(&b1, true);
o.stringPtr(&s1, s3);
QCOMPARE(b1, true);
QCOMPARE(s1, QString::fromLatin1("str3"));
{
ConnectWithReferenceObject o2;
QVERIFY(connect(&o2, &ConnectWithReferenceObject::stringRef, &o2, &ConnectWithReferenceObject::stringSlot1));
o2.stringRef(s1, s2);
QCOMPARE(s1, s3);
QCOMPARE(o2.last, s3);
}
{
ConnectWithReferenceObject o2;
QVERIFY(connect(&o2, &ConnectWithReferenceObject::stringRef, &o2, &ConnectWithReferenceObject::stringSlot2));
o2.stringRef(s1, s2);
QCOMPARE(s1, s3);
QCOMPARE(o2.last, s3);
}
{
ConnectWithReferenceObject o2;
QVERIFY(connect(&o2, &ConnectWithReferenceObject::stringRef, &o2, &ConnectWithReferenceObject::stringSlot3));
o2.stringRef(s1, s2);
QCOMPARE(s1, s3);
QCOMPARE(o2.last, s3);
}
}
class ManyArgumentObject : public QObject {
Q_OBJECT
signals:
void signal1(const QString &);
void signal2(const QString &, const QString &);
void signal3(const QString &, const QString &, const QString &);
void signal4(const QString &, const QString &, const QString &, const QString&);
void signal5(const QString &, const QString &, const QString &, const QString&, const QString&);
void signal6(const QString &, const QString &, const QString &, const QString&, const QString&, const QString&);
public slots:
#define MANYARGUMENT_COMPARE(L) QCOMPARE(L, QString(#L))
void slot1(const QString &a) {
MANYARGUMENT_COMPARE(a);
count++;
}
void slot2(const QString &a, const QString &b) {
MANYARGUMENT_COMPARE(a); MANYARGUMENT_COMPARE(b);
count++;
}
void slot3(const QString &a, const QString &b, const QString &c) {
MANYARGUMENT_COMPARE(a); MANYARGUMENT_COMPARE(b); MANYARGUMENT_COMPARE(c);
count++;
}
void slot4(const QString &a, const QString &b, const QString &c, const QString&d) {
MANYARGUMENT_COMPARE(a); MANYARGUMENT_COMPARE(b); MANYARGUMENT_COMPARE(c);
MANYARGUMENT_COMPARE(d);
count++;
}
void slot5(const QString &a, const QString &b, const QString &c, const QString&d, const QString&e) {
MANYARGUMENT_COMPARE(a); MANYARGUMENT_COMPARE(b); MANYARGUMENT_COMPARE(c);
MANYARGUMENT_COMPARE(d); MANYARGUMENT_COMPARE(e);
count++;
}
void slot6(const QString &a, const QString &b, const QString &c, const QString&d, const QString&e, const QString&f) {
MANYARGUMENT_COMPARE(a); MANYARGUMENT_COMPARE(b); MANYARGUMENT_COMPARE(c);
MANYARGUMENT_COMPARE(d); MANYARGUMENT_COMPARE(e); MANYARGUMENT_COMPARE(f);
count++;
}
public:
int count;
};
namespace ManyArgumentNamespace {
int count;
void slot1(const QString &a) {
MANYARGUMENT_COMPARE(a);
count++;
}
void slot2(const QString &a, const QString &b) {
MANYARGUMENT_COMPARE(a); MANYARGUMENT_COMPARE(b);
count++;
}
void slot3(const QString &a, const QString &b, const QString &c) {
MANYARGUMENT_COMPARE(a); MANYARGUMENT_COMPARE(b); MANYARGUMENT_COMPARE(c);
count++;
}
void slot4(const QString &a, const QString &b, const QString &c, const QString&d) {
MANYARGUMENT_COMPARE(a); MANYARGUMENT_COMPARE(b); MANYARGUMENT_COMPARE(c);
MANYARGUMENT_COMPARE(d);
count++;
}
void slot5(const QString &a, const QString &b, const QString &c, const QString&d, const QString&e) {
MANYARGUMENT_COMPARE(a); MANYARGUMENT_COMPARE(b); MANYARGUMENT_COMPARE(c);
MANYARGUMENT_COMPARE(d); MANYARGUMENT_COMPARE(e);
count++;
}
void slot6(const QString &a, const QString &b, const QString &c, const QString&d, const QString&e, const QString&f) {
MANYARGUMENT_COMPARE(a); MANYARGUMENT_COMPARE(b); MANYARGUMENT_COMPARE(c);
MANYARGUMENT_COMPARE(d); MANYARGUMENT_COMPARE(e); MANYARGUMENT_COMPARE(f);
count++;
}
struct Funct1 {
void operator()(const QString &a) {
MANYARGUMENT_COMPARE(a);
count++;
}
};
struct Funct2 {
void operator()(const QString &a, const QString &b) {
MANYARGUMENT_COMPARE(a); MANYARGUMENT_COMPARE(b);
count++;
}
};
struct Funct3 {
void operator()(const QString &a, const QString &b, const QString &c) {
MANYARGUMENT_COMPARE(a); MANYARGUMENT_COMPARE(b); MANYARGUMENT_COMPARE(c);
count++;
}
};
struct Funct4 {
void operator()(const QString &a, const QString &b, const QString &c, const QString&d) {
MANYARGUMENT_COMPARE(a); MANYARGUMENT_COMPARE(b); MANYARGUMENT_COMPARE(c);
MANYARGUMENT_COMPARE(d);
count++;
}
};
struct Funct5 {
void operator()(const QString &a, const QString &b, const QString &c, const QString&d, const QString&e) {
MANYARGUMENT_COMPARE(a); MANYARGUMENT_COMPARE(b); MANYARGUMENT_COMPARE(c);
MANYARGUMENT_COMPARE(d); MANYARGUMENT_COMPARE(e);
count++;
}
};
struct Funct6 {
void operator()(const QString &a, const QString &b, const QString &c, const QString&d, const QString&e, const QString&f) {
MANYARGUMENT_COMPARE(a); MANYARGUMENT_COMPARE(b); MANYARGUMENT_COMPARE(c);
MANYARGUMENT_COMPARE(d); MANYARGUMENT_COMPARE(e); MANYARGUMENT_COMPARE(f);
count++;
}
};
}
void tst_QObject::connectManyArguments()
{
ManyArgumentObject ob;
ob.count = 0;
ManyArgumentNamespace::count = 0;
connect(&ob, &ManyArgumentObject::signal1, &ob, &ManyArgumentObject::slot1);
connect(&ob, &ManyArgumentObject::signal2, &ob, &ManyArgumentObject::slot2);
connect(&ob, &ManyArgumentObject::signal3, &ob, &ManyArgumentObject::slot3);
connect(&ob, &ManyArgumentObject::signal4, &ob, &ManyArgumentObject::slot4);
connect(&ob, &ManyArgumentObject::signal5, &ob, &ManyArgumentObject::slot5);
connect(&ob, &ManyArgumentObject::signal6, &ob, &ManyArgumentObject::slot6);
connect(&ob, &ManyArgumentObject::signal1, ManyArgumentNamespace::slot1);
connect(&ob, &ManyArgumentObject::signal2, ManyArgumentNamespace::slot2);
connect(&ob, &ManyArgumentObject::signal3, ManyArgumentNamespace::slot3);
connect(&ob, &ManyArgumentObject::signal4, ManyArgumentNamespace::slot4);
connect(&ob, &ManyArgumentObject::signal5, ManyArgumentNamespace::slot5);
connect(&ob, &ManyArgumentObject::signal6, ManyArgumentNamespace::slot6);
connect(&ob, &ManyArgumentObject::signal6, &ob, &ManyArgumentObject::signal5);
connect(&ob, &ManyArgumentObject::signal5, &ob, &ManyArgumentObject::signal4);
connect(&ob, &ManyArgumentObject::signal4, &ob, &ManyArgumentObject::signal3);
connect(&ob, &ManyArgumentObject::signal3, &ob, &ManyArgumentObject::signal2);
connect(&ob, &ManyArgumentObject::signal2, &ob, &ManyArgumentObject::signal1);
emit ob.signal6("a", "b", "c", "d", "e", "f");
QCOMPARE(ob.count, 6);
QCOMPARE(ManyArgumentNamespace::count, 6);
ManyArgumentObject ob2;
ob2.count = 0;
ManyArgumentNamespace::count = 0;
connect(&ob2, &ManyArgumentObject::signal6, &ob2, &ManyArgumentObject::slot1);
connect(&ob2, &ManyArgumentObject::signal6, &ob2, &ManyArgumentObject::slot2);
connect(&ob2, &ManyArgumentObject::signal6, &ob2, &ManyArgumentObject::slot3);
connect(&ob2, &ManyArgumentObject::signal6, &ob2, &ManyArgumentObject::slot4);
connect(&ob2, &ManyArgumentObject::signal6, &ob2, &ManyArgumentObject::slot5);
connect(&ob2, &ManyArgumentObject::signal6, &ob2, &ManyArgumentObject::slot6);
connect(&ob2, &ManyArgumentObject::signal6, ManyArgumentNamespace::slot1);
connect(&ob2, &ManyArgumentObject::signal6, ManyArgumentNamespace::slot2);
connect(&ob2, &ManyArgumentObject::signal6, ManyArgumentNamespace::slot3);
connect(&ob2, &ManyArgumentObject::signal6, ManyArgumentNamespace::slot4);
connect(&ob2, &ManyArgumentObject::signal6, ManyArgumentNamespace::slot5);
connect(&ob2, &ManyArgumentObject::signal6, ManyArgumentNamespace::slot6);
connect(&ob2, &ManyArgumentObject::signal6, ManyArgumentNamespace::Funct1());
connect(&ob2, &ManyArgumentObject::signal6, ManyArgumentNamespace::Funct2());
connect(&ob2, &ManyArgumentObject::signal6, ManyArgumentNamespace::Funct3());
connect(&ob2, &ManyArgumentObject::signal6, ManyArgumentNamespace::Funct4());
connect(&ob2, &ManyArgumentObject::signal6, ManyArgumentNamespace::Funct5());
connect(&ob2, &ManyArgumentObject::signal6, ManyArgumentNamespace::Funct6());
emit ob2.signal6("a", "b", "c", "d", "e", "f");
QCOMPARE(ob2.count, 6);
QCOMPARE(ManyArgumentNamespace::count, 12);
}
class ForwardDeclared;
class ForwardDeclareArguments : public QObject
{
Q_OBJECT
signals:
void mySignal(const ForwardDeclared&);
public slots:
void mySlot(const ForwardDeclared&) {}
};
void tst_QObject::connectForwardDeclare()
{
ForwardDeclareArguments ob;
// it should compile
QVERIFY(connect(&ob, &ForwardDeclareArguments::mySignal, &ob, &ForwardDeclareArguments::mySlot, Qt::QueuedConnection));
}
class NoDefaultConstructor
{
Q_GADGET
public:
NoDefaultConstructor(int) {}
};
class NoDefaultContructorArguments : public QObject
{
Q_OBJECT
signals:
void mySignal(const NoDefaultConstructor&);
public slots:
void mySlot(const NoDefaultConstructor&) {}
};
void tst_QObject::connectNoDefaultConstructorArg()
{
NoDefaultContructorArguments ob;
// it should compile
QVERIFY(connect(&ob, &NoDefaultContructorArguments::mySignal, &ob, &NoDefaultContructorArguments::mySlot, Qt::QueuedConnection));
}
struct MoveOnly
{
int value;
explicit MoveOnly(int v = 1) : value(v) {}
MoveOnly(MoveOnly &&o) : value(o.value) { o.value = -1; }
MoveOnly &operator=(MoveOnly &&o) { value = o.value; o.value = -1; return *this; }
Q_DISABLE_COPY(MoveOnly);
};
class ReturnValue : public QObject {
friend class tst_QObject;
Q_OBJECT
signals:
QVariant returnVariant(int);
QString returnString(int);
int returnInt(int);
void returnVoid(int);
CustomType returnCustomType(int);
MoveOnly returnMoveOnly(int);
QObject *returnPointer();
public slots:
QVariant returnVariantSlot(int i) { return i; }
QString returnStringSlot(int i) { return QString::number(i); }
int returnIntSlot(int i) { return i; }
CustomType returnCustomTypeSlot(int i) { return CustomType(i); }
void returnVoidSlot() {}
int return23() { return 23; }
QString returnHello() { return QStringLiteral("hello"); }
QObject *returnThisSlot1() { return this; }
ReturnValue *returnThisSlot2() { return this; }
MoveOnly returnMoveOnlySlot(int i) { return MoveOnly(i); }
public:
struct VariantFunctor {
QVariant operator()(int i) { return i; }
};
struct CustomTypeFunctor {
CustomType operator()(int i) { return CustomType(i); }
};
struct StringFunctor {
QString operator()(int i) { return QString::number(i); }
};
struct IntFunctor {
int operator()(int i) { return i; }
};
struct VoidFunctor {
void operator()(int) {}
};
struct MoveOnlyFunctor {
MoveOnly operator()(int i) { return MoveOnly(i); }
};
};
QString someFunctionReturningString(int i) {
return '\'' + QString::number(i) + '\'';
}
void tst_QObject::returnValue_data()
{
QTest::addColumn<bool>("isBlockingQueued");
QTest::newRow("DirectConnection") << false;
QTest::newRow("BlockingQueuedConnection") << true;
}
void tst_QObject::returnValue()
{
CheckInstanceCount checker;
QFETCH(bool, isBlockingQueued);
QThread thread;
ReturnValue receiver;
Qt::ConnectionType type = Qt::DirectConnection;
if (isBlockingQueued) {
thread.start();
receiver.moveToThread(&thread);
type = Qt::BlockingQueuedConnection;
}
{ // connected to nothing
CheckInstanceCount checker;
ReturnValue r;
QCOMPARE(emit r.returnVariant(45), QVariant());
QCOMPARE(emit r.returnString(45), QString());
QCOMPARE(emit r.returnInt(45), int());
emit r.returnVoid(45);
QCOMPARE((emit r.returnCustomType(45)).value(), CustomType().value());
QCOMPARE((emit r.returnPointer()), static_cast<QObject *>(0));
QCOMPARE((emit r.returnMoveOnly(666)).value, MoveOnly().value);
}
{ // connected to a slot returning the same type
CheckInstanceCount checker;
ReturnValue r;
QVERIFY(connect(&r, &ReturnValue::returnVariant, &receiver, &ReturnValue::returnVariantSlot, type));
QCOMPARE(emit r.returnVariant(45), QVariant(45));
QVERIFY(connect(&r, &ReturnValue::returnString, &receiver, &ReturnValue::returnStringSlot, type));
QCOMPARE(emit r.returnString(45), QString::fromLatin1("45"));
QVERIFY(connect(&r, &ReturnValue::returnInt, &receiver, &ReturnValue::returnIntSlot, type));
QCOMPARE(emit r.returnInt(45), int(45));
QVERIFY(connect(&r, &ReturnValue::returnCustomType, &receiver, &ReturnValue::returnCustomTypeSlot, type));
QCOMPARE((emit r.returnCustomType(45)).value(), CustomType(45).value());
QVERIFY(connect(&r, &ReturnValue::returnPointer, &receiver, &ReturnValue::returnThisSlot1, type));
QCOMPARE((emit r.returnPointer()), static_cast<QObject *>(&receiver));
QVERIFY(connect(&r, &ReturnValue::returnMoveOnly, &receiver, &ReturnValue::returnMoveOnlySlot, type));
QCOMPARE((emit r.returnMoveOnly(666)).value, 666);
}
if (!isBlockingQueued) { // connected to simple functions or functor
CheckInstanceCount checker;
ReturnValue r;
QVERIFY(connect(&r, &ReturnValue::returnString, someFunctionReturningString));
QCOMPARE(emit r.returnString(49), QString::fromLatin1("'49'"));
ReturnValue::CustomTypeFunctor customTypeFunctor;
QVERIFY(connect(&r, &ReturnValue::returnCustomType, customTypeFunctor));
QCOMPARE((emit r.returnCustomType(49)).value(), CustomType(49).value());
ReturnValue::VariantFunctor variantFunctor;
QVERIFY(connect(&r, &ReturnValue::returnVariant, variantFunctor));
QCOMPARE(emit r.returnVariant(45), QVariant(45));
ReturnValue::IntFunctor intFunctor;
QVERIFY(connect(&r, &ReturnValue::returnInt, intFunctor));
QCOMPARE(emit r.returnInt(45), int(45));
ReturnValue::MoveOnlyFunctor moveOnlyFunctor;
QVERIFY(connect(&r, &ReturnValue::returnMoveOnly, moveOnlyFunctor));
QCOMPARE((emit r.returnMoveOnly(666)).value, 666);
}
{ // connected to a slot with different type
CheckInstanceCount checker;
ReturnValue r;
QVERIFY(connect(&r, &ReturnValue::returnVariant, &receiver, &ReturnValue::returnStringSlot, type));
QCOMPARE(emit r.returnVariant(48), QVariant(QString::fromLatin1("48")));
QVERIFY(connect(&r, &ReturnValue::returnCustomType, &receiver, &ReturnValue::returnIntSlot, type));
QCOMPARE((emit r.returnCustomType(48)).value(), CustomType(48).value());
QVERIFY(connect(&r, &ReturnValue::returnVoid, &receiver, &ReturnValue::returnCustomTypeSlot, type));
emit r.returnVoid(48);
QVERIFY(connect(&r, &ReturnValue::returnPointer, &receiver, &ReturnValue::returnThisSlot2, type));
QCOMPARE((emit r.returnPointer()), static_cast<QObject *>(&receiver));
}
if (!isBlockingQueued) { // connected to functor with different type
CheckInstanceCount checker;
ReturnValue r;
ReturnValue::CustomTypeFunctor customTypeFunctor;
QVERIFY(connect(&r, &ReturnValue::returnCustomType, customTypeFunctor));
QCOMPARE((emit r.returnCustomType(49)).value(), CustomType(49).value());
ReturnValue::StringFunctor stringFunctor;
QVERIFY(connect(&r, &ReturnValue::returnVariant, stringFunctor));
QCOMPARE(emit r.returnVariant(45), QVariant(QString::fromLatin1("45")));
}
{ // connected to a void
CheckInstanceCount checker;
ReturnValue r;
QVERIFY(connect(&r, &ReturnValue::returnVariant, &receiver, &ReturnValue::returnVoidSlot, type));
QCOMPARE(emit r.returnVariant(45), QVariant());
QVERIFY(connect(&r, &ReturnValue::returnString, &receiver, &ReturnValue::returnVoidSlot, type));
QCOMPARE(emit r.returnString(45), QString());
QVERIFY(connect(&r, &ReturnValue::returnInt, &receiver, &ReturnValue::returnVoidSlot, type));
QCOMPARE(emit r.returnInt(45), int());
QVERIFY(connect(&r, &ReturnValue::returnCustomType, &receiver, &ReturnValue::returnVoidSlot, type));
QCOMPARE((emit r.returnCustomType(45)).value(), CustomType().value());
QVERIFY(connect(&r, &ReturnValue::returnPointer, &receiver, &ReturnValue::returnVoidSlot, type));
QCOMPARE((emit r.returnPointer()), static_cast<QObject *>(0));
QVERIFY(connect(&r, &ReturnValue::returnMoveOnly, &receiver, &ReturnValue::returnVoidSlot, type));
QCOMPARE((emit r.returnMoveOnly(666)).value, MoveOnly().value);
}
if (!isBlockingQueued) {
// queued connection should not forward the return value
CheckInstanceCount checker;
ReturnValue r;
QVERIFY(connect(&r, &ReturnValue::returnVariant, &receiver, &ReturnValue::returnVariantSlot, Qt::QueuedConnection));
QCOMPARE(emit r.returnVariant(45), QVariant());
QVERIFY(connect(&r, &ReturnValue::returnString, &receiver, &ReturnValue::returnStringSlot, Qt::QueuedConnection));
QCOMPARE(emit r.returnString(45), QString());
QVERIFY(connect(&r, &ReturnValue::returnInt, &receiver, &ReturnValue::returnIntSlot, Qt::QueuedConnection));
QCOMPARE(emit r.returnInt(45), int());
QVERIFY(connect(&r, &ReturnValue::returnCustomType, &receiver, &ReturnValue::returnCustomTypeSlot, Qt::QueuedConnection));
QCOMPARE((emit r.returnCustomType(45)).value(), CustomType().value());
QVERIFY(connect(&r, &ReturnValue::returnPointer, &receiver, &ReturnValue::returnThisSlot1, Qt::QueuedConnection));
QCOMPARE((emit r.returnPointer()), static_cast<QObject *>(0));
QVERIFY(connect(&r, &ReturnValue::returnMoveOnly, &receiver, &ReturnValue::returnMoveOnlySlot, Qt::QueuedConnection));
QCOMPARE((emit r.returnMoveOnly(666)).value, MoveOnly().value);
QCoreApplication::processEvents();
QVERIFY(connect(&r, &ReturnValue::returnVariant, &receiver, &ReturnValue::returnStringSlot, Qt::QueuedConnection));
QCOMPARE(emit r.returnVariant(48), QVariant());
QVERIFY(connect(&r, &ReturnValue::returnCustomType, &receiver, &ReturnValue::returnIntSlot, Qt::QueuedConnection));
QCOMPARE((emit r.returnCustomType(48)).value(), CustomType().value());
QVERIFY(connect(&r, &ReturnValue::returnVoid, &receiver, &ReturnValue::returnCustomTypeSlot, Qt::QueuedConnection));
emit r.returnVoid(48);
QCoreApplication::processEvents();
}
{ // connected to many slots
ReturnValue::VoidFunctor voidFunctor;
ReturnValue::IntFunctor intFunctor;
ReturnValue r;
QVERIFY(connect(&r, &ReturnValue::returnVariant, &receiver, &ReturnValue::returnVariantSlot, type));
QCOMPARE(emit r.returnVariant(45), QVariant(45));
QVERIFY(connect(&r, &ReturnValue::returnVariant, &receiver, &ReturnValue::return23, type));
QCOMPARE(emit r.returnVariant(45), QVariant(23));
QVERIFY(connect(&r, &ReturnValue::returnVariant, &receiver, &ReturnValue::returnVoidSlot, type));
QCOMPARE(emit r.returnVariant(45), QVariant(23));
QVERIFY(connect(&r, &ReturnValue::returnVariant, &receiver, &ReturnValue::returnHello, type));
QCOMPARE(emit r.returnVariant(45), QVariant(QStringLiteral("hello")));
QVERIFY(connect(&r, &ReturnValue::returnVariant, voidFunctor));
QCOMPARE(emit r.returnVariant(45), QVariant(QStringLiteral("hello")));
QVERIFY(connect(&r, &ReturnValue::returnVariant, intFunctor));
QCOMPARE(emit r.returnVariant(45), QVariant(45));
QVERIFY(connect(&r, &ReturnValue::returnVariant, &receiver, &ReturnValue::return23, Qt::QueuedConnection));
QCOMPARE(emit r.returnVariant(45), QVariant(45));
QCOMPARE(emit r.returnInt(45), int());
QVERIFY(connect(&r, &ReturnValue::returnInt, &receiver, &ReturnValue::returnVoidSlot, type));
QCOMPARE(emit r.returnInt(45), int());
QVERIFY(connect(&r, &ReturnValue::returnInt, &receiver, &ReturnValue::returnIntSlot, type));
QCOMPARE(emit r.returnInt(45), int(45));
QVERIFY(connect(&r, &ReturnValue::returnInt, &receiver, &ReturnValue::return23, type));
QCOMPARE(emit r.returnInt(45), int(23));
QVERIFY(connect(&r, &ReturnValue::returnInt, voidFunctor));
QCOMPARE(emit r.returnInt(45), int(23));
QVERIFY(connect(&r, &ReturnValue::returnInt, intFunctor));
QCOMPARE(emit r.returnInt(45), int(45));
QVERIFY(connect(&r, &ReturnValue::returnInt, &receiver, &ReturnValue::return23, Qt::QueuedConnection));
QCOMPARE(emit r.returnInt(45), int(45));
QCoreApplication::processEvents();
}
if (isBlockingQueued) {
thread.quit();
thread.wait();
}
}
void tst_QObject::returnValue2_data()
{ returnValue_data(); }
//String based syntax
void tst_QObject::returnValue2()
{
CheckInstanceCount checker;
QFETCH(bool, isBlockingQueued);
QThread thread;
ReturnValue receiver;
Qt::ConnectionType type = Qt::DirectConnection;
if (isBlockingQueued) {
thread.start();
receiver.moveToThread(&thread);
type = Qt::BlockingQueuedConnection;
}
{ // connected to a simple slot
CheckInstanceCount checker;
ReturnValue r;
QVERIFY(connect(&r, SIGNAL(returnVariant(int)), &receiver, SLOT(returnVariantSlot(int)), type));
QCOMPARE(emit r.returnVariant(45), QVariant(45));
QVERIFY(connect(&r, SIGNAL(returnString(int)), &receiver, SLOT(returnStringSlot(int)), type));
QCOMPARE(emit r.returnString(45), QString(QStringLiteral("45")));
QVERIFY(connect(&r, SIGNAL(returnInt(int)), &receiver, SLOT(returnIntSlot(int)), type));
QCOMPARE(emit r.returnInt(45), int(45));
QVERIFY(connect(&r, SIGNAL(returnCustomType(int)), &receiver, SLOT(returnCustomTypeSlot(int)), type));
QCOMPARE((emit r.returnCustomType(45)).value(), CustomType(45).value());
QVERIFY(connect(&r, SIGNAL(returnMoveOnly(int)), &receiver, SLOT(returnMoveOnlySlot(int)), type));
QCOMPARE((emit r.returnMoveOnly(45)).value, 45);
}
{ // connected to a slot returning void
CheckInstanceCount checker;
ReturnValue r;
QVERIFY(connect(&r, SIGNAL(returnVariant(int)), &receiver, SLOT(returnVoidSlot()), type));
QCOMPARE(emit r.returnVariant(45), QVariant());
QVERIFY(connect(&r, SIGNAL(returnString(int)), &receiver, SLOT(returnVoidSlot()), type));
QCOMPARE(emit r.returnString(45), QString());
QVERIFY(connect(&r, SIGNAL(returnInt(int)), &receiver, SLOT(returnVoidSlot()), type));
QCOMPARE(emit r.returnInt(45), int());
QVERIFY(connect(&r, SIGNAL(returnCustomType(int)), &receiver, SLOT(returnVoidSlot()), type));
QCOMPARE((emit r.returnCustomType(45)).value(), CustomType().value());
QVERIFY(connect(&r, SIGNAL(returnMoveOnly(int)), &receiver, SLOT(returnVoidSlot()), type));
QCOMPARE((emit r.returnMoveOnly(45)).value, MoveOnly().value);
}
if (!isBlockingQueued) {
// queued connection should not forward the return value
CheckInstanceCount checker;
ReturnValue r;
QVERIFY(connect(&r, SIGNAL(returnVariant(int)), &receiver, SLOT(returnVariantSlot(int)), Qt::QueuedConnection));
QCOMPARE(emit r.returnVariant(45), QVariant());
QVERIFY(connect(&r, SIGNAL(returnString(int)), &receiver, SLOT(returnStringSlot(int)), Qt::QueuedConnection));
QCOMPARE(emit r.returnString(45), QString());
QVERIFY(connect(&r, SIGNAL(returnInt(int)), &receiver, SLOT(returnIntSlot(int)), Qt::QueuedConnection));
QCOMPARE(emit r.returnInt(45), int());
QVERIFY(connect(&r, SIGNAL(returnCustomType(int)), &receiver, SLOT(returnCustomTypeSlot(int)), Qt::QueuedConnection));
QCOMPARE((emit r.returnCustomType(45)).value(), CustomType().value());
QVERIFY(connect(&r, SIGNAL(returnMoveOnly(int)), &receiver, SLOT(returnMoveOnlySlot(int)), Qt::QueuedConnection));
QCOMPARE((emit r.returnMoveOnly(45)).value, MoveOnly().value);
QCoreApplication::processEvents();
//Queued conneciton with different return type should be safe
QVERIFY(connect(&r, SIGNAL(returnVariant(int)), &receiver, SLOT(returnStringSlot(int)), Qt::QueuedConnection));
QCOMPARE(emit r.returnVariant(48), QVariant());
QVERIFY(connect(&r, SIGNAL(returnCustomType(int)), &receiver, SLOT(returnIntSlot(int)), Qt::QueuedConnection));
QCOMPARE((emit r.returnCustomType(48)).value(), CustomType().value());
QVERIFY(connect(&r, SIGNAL(returnVoid(int)), &receiver, SLOT(returnCustomTypeSlot(int)), Qt::QueuedConnection));
emit r.returnVoid(48);
QCoreApplication::processEvents();
}
{ // connected to many slots
ReturnValue r;
QVERIFY(connect(&r, SIGNAL(returnInt(int)), &receiver, SLOT(returnIntSlot(int)), type));
QCOMPARE(emit r.returnInt(45), int(45));
QVERIFY(connect(&r, SIGNAL(returnInt(int)), &receiver, SLOT(returnVoidSlot()), type));
QCOMPARE(emit r.returnInt(45), int(45));
QVERIFY(connect(&r, SIGNAL(returnInt(int)), &receiver, SLOT(return23()), type));
QCOMPARE(emit r.returnInt(45), int(23));
QVERIFY(connect(&r, SIGNAL(returnInt(int)), &receiver, SLOT(returnIntSlot(int)), Qt::QueuedConnection));
QCOMPARE(emit r.returnInt(45), int(23));
QVERIFY(connect(&r, SIGNAL(returnString(int)), &receiver, SLOT(returnStringSlot(int)), type));
QCOMPARE(emit r.returnString(45), QString(QStringLiteral("45")));
QVERIFY(connect(&r, SIGNAL(returnString(int)), &receiver, SLOT(returnVoidSlot()), type));
QCOMPARE(emit r.returnString(45), QString(QStringLiteral("45")));
QVERIFY(connect(&r, SIGNAL(returnString(int)), &receiver, SLOT(returnHello()), type));
QCOMPARE(emit r.returnString(45), QString(QStringLiteral("hello")));
QVERIFY(connect(&r, SIGNAL(returnString(int)), &receiver, SLOT(returnStringSlot(int)), Qt::QueuedConnection));
QCOMPARE(emit r.returnString(45), QString(QStringLiteral("hello")));
}
if (isBlockingQueued) {
thread.quit();
thread.wait();
}
}
class VirtualSlotsObjectBase : public QObject {
Q_OBJECT
public slots:
virtual void slot1() {
base_counter1++;
}
public:
VirtualSlotsObjectBase() : base_counter1(0) {}
int base_counter1;
signals:
void signal1();
};
class VirtualSlotsObject : public VirtualSlotsObjectBase {
Q_OBJECT
public slots:
virtual void slot1() {
derived_counter1++;
}
public:
VirtualSlotsObject() : derived_counter1(0) {}
int derived_counter1;
};
void tst_QObject::connectVirtualSlots()
{
VirtualSlotsObject obj;
QVERIFY( QObject::connect(&obj, &VirtualSlotsObjectBase::signal1, &obj, &VirtualSlotsObjectBase::slot1, Qt::UniqueConnection));
QVERIFY(!QObject::connect(&obj, &VirtualSlotsObjectBase::signal1, &obj, &VirtualSlotsObjectBase::slot1, Qt::UniqueConnection));
emit obj.signal1();
QCOMPARE(obj.base_counter1, 0);
QCOMPARE(obj.derived_counter1, 1);
QVERIFY(QObject::disconnect(&obj, &VirtualSlotsObjectBase::signal1, &obj, &VirtualSlotsObjectBase::slot1));
QVERIFY(!QObject::disconnect(&obj, &VirtualSlotsObjectBase::signal1, &obj, &VirtualSlotsObjectBase::slot1));
emit obj.signal1();
QCOMPARE(obj.base_counter1, 0);
QCOMPARE(obj.derived_counter1, 1);
/* the C++ standard say the comparison between pointer to virtual member function is unspecified
QVERIFY( QObject::connect(&obj, &VirtualSlotsObjectBase::signal1, &obj, &VirtualSlotsObjectBase::slot1, Qt::UniqueConnection));
QVERIFY(!QObject::connect(&obj, &VirtualSlotsObjectBase::signal1, &obj, &VirtualSlotsObject::slot1, Qt::UniqueConnection));
*/
}
struct VirtualBase
{
int virtual_base_count;
VirtualBase() : virtual_base_count(0) {}
virtual ~VirtualBase() {}
virtual void slot2() = 0;
};
class ObjectWithVirtualBase : public VirtualSlotsObject, public virtual VirtualBase
{
Q_OBJECT
public:
ObjectWithVirtualBase() : regular_call_count(0), derived_counter2(0) {}
int regular_call_count;
int derived_counter2;
public slots:
void regularSlot() { ++regular_call_count; }
virtual void slot1() { ++derived_counter2; }
virtual void slot2() { ++virtual_base_count; }
};
struct NormalBase
{
QByteArray lastCalled;
virtual ~NormalBase() {}
virtual void virtualBaseSlot() { lastCalled = "virtualBaseSlot"; }
void normalBaseSlot() { lastCalled = "normalBaseSlot"; }
};
class ObjectWithMultiInheritance : public VirtualSlotsObject, public NormalBase
{
Q_OBJECT
};
// Normally, the class that inherit QObject always must go first, because of the way qobject_cast
// work, and moc checks for that. But if we don't use Q_OBJECT, this should work
class ObjectWithMultiInheritance2 : public NormalBase, public VirtualSlotsObject
{
// no QObject as QObject always must go first
// Q_OBJECT
};
// VMI = Virtual or Multiple Inheritance
// (in this case, both)
void tst_QObject::connectSlotsVMIClass()
{
// test connecting by the base
{
ObjectWithVirtualBase obj;
QVERIFY( QObject::connect(&obj, &VirtualSlotsObjectBase::signal1, &obj, &VirtualSlotsObjectBase::slot1, Qt::UniqueConnection));
QVERIFY(!QObject::connect(&obj, &VirtualSlotsObjectBase::signal1, &obj, &VirtualSlotsObjectBase::slot1, Qt::UniqueConnection));
emit obj.signal1();
QCOMPARE(obj.base_counter1, 0);
QCOMPARE(obj.derived_counter1, 0);
QCOMPARE(obj.derived_counter2, 1);
QCOMPARE(obj.virtual_base_count, 0);
QVERIFY(QObject::disconnect(&obj, &VirtualSlotsObjectBase::signal1, &obj, &VirtualSlotsObjectBase::slot1));
QVERIFY(!QObject::disconnect(&obj, &VirtualSlotsObjectBase::signal1, &obj, &VirtualSlotsObjectBase::slot1));
emit obj.signal1();
QCOMPARE(obj.base_counter1, 0);
QCOMPARE(obj.derived_counter1, 0);
QCOMPARE(obj.derived_counter2, 1);
QCOMPARE(obj.virtual_base_count, 0);
}
// test connecting with the actual class
{
ObjectWithVirtualBase obj;
QVERIFY( QObject::connect(&obj, &VirtualSlotsObjectBase::signal1, &obj, &ObjectWithVirtualBase::regularSlot, Qt::UniqueConnection));
QVERIFY(!QObject::connect(&obj, &VirtualSlotsObjectBase::signal1, &obj, &ObjectWithVirtualBase::regularSlot, Qt::UniqueConnection));
QVERIFY( QObject::connect(&obj, &VirtualSlotsObjectBase::signal1, &obj, &ObjectWithVirtualBase::slot1, Qt::UniqueConnection));
QVERIFY(!QObject::connect(&obj, &VirtualSlotsObjectBase::signal1, &obj, &ObjectWithVirtualBase::slot1, Qt::UniqueConnection));
emit obj.signal1();
QCOMPARE(obj.base_counter1, 0);
QCOMPARE(obj.derived_counter1, 0);
QCOMPARE(obj.derived_counter2, 1);
QCOMPARE(obj.regular_call_count, 1);
QCOMPARE(obj.virtual_base_count, 0);
QVERIFY( QObject::disconnect(&obj, &VirtualSlotsObjectBase::signal1, &obj, &ObjectWithVirtualBase::regularSlot));
QVERIFY(!QObject::disconnect(&obj, &VirtualSlotsObjectBase::signal1, &obj, &ObjectWithVirtualBase::regularSlot));
QVERIFY( QObject::disconnect(&obj, &VirtualSlotsObjectBase::signal1, &obj, &ObjectWithVirtualBase::slot1));
QVERIFY(!QObject::disconnect(&obj, &VirtualSlotsObjectBase::signal1, &obj, &ObjectWithVirtualBase::slot1));
emit obj.signal1();
QCOMPARE(obj.base_counter1, 0);
QCOMPARE(obj.derived_counter1, 0);
QCOMPARE(obj.derived_counter2, 1);
QCOMPARE(obj.regular_call_count, 1);
QCOMPARE(obj.virtual_base_count, 0);
/* the C++ standard say the comparison between pointer to virtual member function is unspecified
QVERIFY( QObject::connect(&obj, &VirtualSlotsObjectBase::signal1, &obj, &VirtualSlotsObjectBase::slot1, Qt::UniqueConnection));
QVERIFY(!QObject::connect(&obj, &VirtualSlotsObjectBase::signal1, &obj, &ObjectWithVirtualBase::slot1, Qt::UniqueConnection));
*/
}
// test connecting a slot that is virtual from the virtual base
{
ObjectWithVirtualBase obj;
QVERIFY( QObject::connect(&obj, &VirtualSlotsObjectBase::signal1, &obj, &ObjectWithVirtualBase::slot2, Qt::UniqueConnection));
QVERIFY(!QObject::connect(&obj, &VirtualSlotsObjectBase::signal1, &obj, &ObjectWithVirtualBase::slot2, Qt::UniqueConnection));
emit obj.signal1();
QCOMPARE(obj.base_counter1, 0);
QCOMPARE(obj.derived_counter1, 0);
QCOMPARE(obj.derived_counter2, 0);
QCOMPARE(obj.virtual_base_count, 1);
QCOMPARE(obj.regular_call_count, 0);
QVERIFY( QObject::disconnect(&obj, &VirtualSlotsObjectBase::signal1, &obj, &ObjectWithVirtualBase::slot2));
QVERIFY(!QObject::disconnect(&obj, &VirtualSlotsObjectBase::signal1, &obj, &ObjectWithVirtualBase::slot2));
emit obj.signal1();
QCOMPARE(obj.base_counter1, 0);
QCOMPARE(obj.derived_counter1, 0);
QCOMPARE(obj.derived_counter2, 0);
QCOMPARE(obj.virtual_base_count, 1);
QCOMPARE(obj.regular_call_count, 0);
}
// test connecting a slot that is virtual within the second base
{
ObjectWithMultiInheritance obj;
void (ObjectWithMultiInheritance::*slot)() = &ObjectWithMultiInheritance::virtualBaseSlot;
QVERIFY( QObject::connect(&obj, &VirtualSlotsObjectBase::signal1, &obj, slot, Qt::UniqueConnection));
QVERIFY(!QObject::connect(&obj, &VirtualSlotsObjectBase::signal1, &obj, slot, Qt::UniqueConnection));
emit obj.signal1();
QCOMPARE(obj.base_counter1, 0);
QCOMPARE(obj.derived_counter1, 0);
QCOMPARE(obj.lastCalled, QByteArray("virtualBaseSlot"));
obj.lastCalled.clear();
QVERIFY( QObject::disconnect(&obj, &VirtualSlotsObjectBase::signal1, &obj, slot));
QVERIFY(!QObject::disconnect(&obj, &VirtualSlotsObjectBase::signal1, &obj, slot));
emit obj.signal1();
QCOMPARE(obj.base_counter1, 0);
QCOMPARE(obj.derived_counter1, 0);
QCOMPARE(obj.lastCalled, QByteArray());
}
// test connecting a slot that is not virtual within the second base
{
ObjectWithMultiInheritance obj;
void (ObjectWithMultiInheritance::*slot)() = &ObjectWithMultiInheritance::normalBaseSlot;
QVERIFY( QObject::connect(&obj, &VirtualSlotsObjectBase::signal1, &obj, slot, Qt::UniqueConnection));
QVERIFY(!QObject::connect(&obj, &VirtualSlotsObjectBase::signal1, &obj, slot, Qt::UniqueConnection));
emit obj.signal1();
QCOMPARE(obj.base_counter1, 0);
QCOMPARE(obj.derived_counter1, 0);
QCOMPARE(obj.lastCalled, QByteArray("normalBaseSlot"));
obj.lastCalled.clear();
QVERIFY( QObject::disconnect(&obj, &VirtualSlotsObjectBase::signal1, &obj, slot));
QVERIFY(!QObject::disconnect(&obj, &VirtualSlotsObjectBase::signal1, &obj, slot));
emit obj.signal1();
QCOMPARE(obj.base_counter1, 0);
QCOMPARE(obj.derived_counter1, 0);
QCOMPARE(obj.lastCalled, QByteArray());
}
// test connecting a slot within the first non-QObject base
{
ObjectWithMultiInheritance2 obj;
void (ObjectWithMultiInheritance2::*slot)() = &ObjectWithMultiInheritance2::normalBaseSlot;
QVERIFY( QObject::connect(&obj, &VirtualSlotsObjectBase::signal1, &obj, slot, Qt::UniqueConnection));
QVERIFY(!QObject::connect(&obj, &VirtualSlotsObjectBase::signal1, &obj, slot, Qt::UniqueConnection));
emit obj.signal1();
QCOMPARE(obj.base_counter1, 0);
QCOMPARE(obj.derived_counter1, 0);
QCOMPARE(obj.lastCalled, QByteArray("normalBaseSlot"));
obj.lastCalled.clear();
QVERIFY( QObject::disconnect(&obj, &VirtualSlotsObjectBase::signal1, &obj, slot));
QVERIFY(!QObject::disconnect(&obj, &VirtualSlotsObjectBase::signal1, &obj, slot));
emit obj.signal1();
QCOMPARE(obj.base_counter1, 0);
QCOMPARE(obj.derived_counter1, 0);
QCOMPARE(obj.lastCalled, QByteArray());
}
// test connecting a slot within the second QObject base
{
ObjectWithMultiInheritance2 obj;
void (ObjectWithMultiInheritance2::*slot)() = &ObjectWithMultiInheritance2::slot1;
QVERIFY( QObject::connect(&obj, &VirtualSlotsObjectBase::signal1, &obj, slot, Qt::UniqueConnection));
QVERIFY(!QObject::connect(&obj, &VirtualSlotsObjectBase::signal1, &obj, slot, Qt::UniqueConnection));
emit obj.signal1();
QCOMPARE(obj.base_counter1, 0);
QCOMPARE(obj.derived_counter1, 1);
QCOMPARE(obj.lastCalled, QByteArray());
QVERIFY( QObject::disconnect(&obj, &VirtualSlotsObjectBase::signal1, &obj, slot));
QVERIFY(!QObject::disconnect(&obj, &VirtualSlotsObjectBase::signal1, &obj, slot));
emit obj.signal1();
QCOMPARE(obj.base_counter1, 0);
QCOMPARE(obj.derived_counter1, 1);
QCOMPARE(obj.lastCalled, QByteArray());
}
}
#ifndef QT_BUILD_INTERNAL
void tst_QObject::connectPrivateSlots()
{QSKIP("Needs QT_BUILD_INTERNAL");}
#else
class ConnectToPrivateSlotPrivate;
class ConnectToPrivateSlot :public QObject {
Q_OBJECT
public:
ConnectToPrivateSlot();
void test(SenderObject *obj1) ;
Q_DECLARE_PRIVATE(ConnectToPrivateSlot)
};
class ConnectToPrivateSlotPrivate : public QObjectPrivate {
Q_DECLARE_PUBLIC(ConnectToPrivateSlot)
public:
int receivedCount;
QVariant receivedValue;
void thisIsAPrivateSlot() {
receivedCount++;
};
void thisIsAPrivateSlotWithArg(const QVariant &v) {
receivedCount++;
receivedValue = v;
};
};
ConnectToPrivateSlot::ConnectToPrivateSlot(): QObject(*new ConnectToPrivateSlotPrivate) {}
void ConnectToPrivateSlot::test(SenderObject* obj1) {
Q_D(ConnectToPrivateSlot);
d->receivedCount = 0;
QVERIFY(QObjectPrivate::connect(obj1, &SenderObject::signal1, d, &ConnectToPrivateSlotPrivate::thisIsAPrivateSlot));
QVERIFY(QObjectPrivate::connect(obj1, &SenderObject::signal7, d, &ConnectToPrivateSlotPrivate::thisIsAPrivateSlotWithArg));
QCOMPARE(d->receivedCount, 0);
obj1->signal1();
QCOMPARE(d->receivedCount, 1);
QCOMPARE(d->receivedValue, QVariant());
obj1->signal7(666, QLatin1String("_"));
QCOMPARE(d->receivedCount, 2);
QCOMPARE(d->receivedValue, QVariant(666));
QVERIFY(QObjectPrivate::connect(obj1, &SenderObject::signal2, d, &ConnectToPrivateSlotPrivate::thisIsAPrivateSlot, Qt::UniqueConnection));
QVERIFY(!QObjectPrivate::connect(obj1, &SenderObject::signal2, d, &ConnectToPrivateSlotPrivate::thisIsAPrivateSlot, Qt::UniqueConnection));
obj1->signal2();
QCOMPARE(d->receivedCount, 3);
QVERIFY(QObjectPrivate::disconnect(obj1, &SenderObject::signal2, d, &ConnectToPrivateSlotPrivate::thisIsAPrivateSlot));
obj1->signal2();
QCOMPARE(d->receivedCount, 3);
QVERIFY(!QObjectPrivate::disconnect(obj1, &SenderObject::signal2, d, &ConnectToPrivateSlotPrivate::thisIsAPrivateSlot));
}
void tst_QObject::connectPrivateSlots()
{
SenderObject sender;
{
ConnectToPrivateSlot o;
o.test(&sender);
}
sender.signal7(777, QLatin1String("check that deleting the object properly disconnected"));
sender.signal1();
}
#endif
struct SlotFunctor
{
void operator()() {}
};
struct SlotFunctorString
{
void operator()(const QString &) {}
};
void tst_QObject::connectFunctorArgDifference()
{
QTimer timer;
// Compile-time tests that the connection is successful.
connect(&timer, &QTimer::timeout, SlotFunctor());
connect(&timer, &QTimer::objectNameChanged, SlotFunctorString());
connect(qApp, &QCoreApplication::aboutToQuit, SlotFunctor());
connect(&timer, &QTimer::objectNameChanged, SlotFunctor());
QStringListModel model;
connect(&model, &QStringListModel::rowsInserted, SlotFunctor());
connect(&timer, &QTimer::timeout, [=](){});
connect(&timer, &QTimer::objectNameChanged, [=](const QString &){});
connect(qApp, &QCoreApplication::aboutToQuit, [=](){});
connect(&timer, &QTimer::objectNameChanged, [=](){});
connect(&model, &QStringListModel::rowsInserted, [=](){});
connect(&model, &QStringListModel::rowsInserted, [=](const QModelIndex &){});
QVERIFY(true);
}
class ContextObject : public QObject
{
Q_OBJECT
public:
void compareSender(QObject *s) { QCOMPARE(s, sender()); }
};
struct SlotArgFunctor
{
SlotArgFunctor(int *s) : status(s), context(nullptr), sender(nullptr) {}
SlotArgFunctor(ContextObject *context, QObject *sender, int *s) : status(s), context(context), sender(sender) {}
void operator()() { *status = 2; if (context) context->compareSender(sender); }
protected:
int *status;
ContextObject *context;
QObject *sender;
};
void tst_QObject::connectFunctorQueued()
{
int status = 1;
SenderObject obj;
QEventLoop e;
connect(&obj, &SenderObject::signal1, this, SlotArgFunctor(&status), Qt::QueuedConnection);
connect(&obj, &SenderObject::signal1, &e, &QEventLoop::quit, Qt::QueuedConnection);
obj.emitSignal1();
QCOMPARE(status, 1);
e.exec();
QCOMPARE(status, 2);
status = 1;
connect(&obj, &SenderObject::signal1, this, [&status] { status = 2; }, Qt::QueuedConnection);
obj.emitSignal1();
QCOMPARE(status, 1);
e.exec();
QCOMPARE(status, 2);
}
void tst_QObject::connectFunctorWithContext()
{
int status = 1;
SenderObject obj;
ContextObject *context = new ContextObject;
QEventLoop e;
connect(&obj, &SenderObject::signal1, context, SlotArgFunctor(&status));
connect(&obj, &SenderObject::signal1, &e, &QEventLoop::quit, Qt::QueuedConnection);
// When the context gets deleted, the connection should decay and the signal shouldn't trigger
// The connection is queued to make sure the destroyed signal propagates correctly and
// cuts the connection.
connect(context, &QObject::destroyed, &obj, &SenderObject::signal1, Qt::QueuedConnection);
context->deleteLater();
QCOMPARE(status, 1);
e.exec();
QCOMPARE(status, 1);
// Check the sender arg is set correctly in the context
context = new ContextObject;
connect(&obj, &SenderObject::signal1, context,
SlotArgFunctor(context, &obj, &status), Qt::QueuedConnection);
obj.emitSignal1();
QCOMPARE(status, 1);
e.exec();
QCOMPARE(status, 2);
status = 1;
connect(&obj, &SenderObject::signal1, this, [this, &status, &obj] { status = 2; QCOMPARE(sender(), &obj); }, Qt::QueuedConnection);
obj.emitSignal1();
QCOMPARE(status, 1);
e.exec();
QCOMPARE(status, 2);
// Free
context->deleteLater();
}
class StatusChanger : public QObject
{
Q_OBJECT
public:
StatusChanger(int *status) : m_status(status)
{
}
~StatusChanger()
{
*m_status = 2;
}
private:
int *m_status;
};
class DispatcherWatcher : public QObject
{
Q_OBJECT
public:
DispatcherWatcher(QEventLoop &e, int *statusAwake, int *statusAboutToBlock) :
m_eventLoop(&e),
m_statusAwake(statusAwake),
m_statusAboutToBlock(statusAboutToBlock),
m_aboutToBlocks(0),
m_awakes(0)
{
awake = new StatusChanger(statusAwake);
abouttoblock = new StatusChanger(statusAboutToBlock);
QCOMPARE(*statusAwake, 1);
QCOMPARE(*statusAboutToBlock, 1);
connect(QAbstractEventDispatcher::instance(), SIGNAL(awake()), this, SLOT(onAwake()));
connect(QAbstractEventDispatcher::instance(), SIGNAL(aboutToBlock()), this, SLOT(onAboutToBlock()));
}
~DispatcherWatcher()
{
if (awake)
awake->deleteLater();
if (abouttoblock)
abouttoblock->deleteLater();
}
public slots:
// The order of these 2 handlers differs on different event dispatchers
void onAboutToBlock()
{
if (abouttoblock) {
abouttoblock->deleteLater();
abouttoblock = 0;
}
++m_aboutToBlocks;
}
void onAwake()
{
if (awake) {
awake->deleteLater();
awake = 0;
}
++m_awakes;
}
void onSignal1()
{
// Status check. At this point the event loop should have spinned enough to delete all the objects.
QCOMPARE(*m_statusAwake, 2);
QCOMPARE(*m_statusAboutToBlock, 2);
QMetaObject::invokeMethod(m_eventLoop, "quit", Qt::QueuedConnection);
}
private:
StatusChanger *awake;
StatusChanger *abouttoblock;
QEventLoop *m_eventLoop;
int *m_statusAwake;
int *m_statusAboutToBlock;
int m_aboutToBlocks;
int m_awakes;
};
void tst_QObject::deleteLaterInAboutToBlockHandler()
{
int statusAwake = 1;
int statusAboutToBlock = 1;
QEventLoop e;
DispatcherWatcher dw(e, &statusAwake, &statusAboutToBlock);
QTimer::singleShot(2000, &dw, &DispatcherWatcher::onSignal1);
QCOMPARE(statusAwake, 1);
QCOMPARE(statusAboutToBlock, 1);
e.exec();
QCOMPARE(statusAwake, 2);
QCOMPARE(statusAboutToBlock, 2);
}
void tst_QObject::connectFunctorWithContextUnique()
{
// Qt::UniqueConnections currently don't work for functors, but we need to
// be sure that they don't crash. If that is implemented, change this test.
SenderObject sender;
ReceiverObject receiver;
QObject::connect(&sender, &SenderObject::signal1, &receiver, &ReceiverObject::slot1);
receiver.count_slot1 = 0;
QObject::connect(&sender, &SenderObject::signal1, &receiver, SlotFunctor(), Qt::UniqueConnection);
sender.emitSignal1();
QCOMPARE(receiver.count_slot1, 1);
}
class MyFunctor
{
public:
explicit MyFunctor(QObject *objectToDisconnect)
: m_objectToDisconnect(objectToDisconnect)
{}
~MyFunctor() {
playWithObjects();
}
void operator()() {
// This will cause the slot object associated with this functor to be destroyed after
// this function returns. That in turn will destroy this functor.
// If our dtor runs with the signalSlotLock held, the bunch of connect()
// performed there will deadlock trying to lock that lock again.
m_objectToDisconnect->disconnect();
}
private:
QObject *m_objectToDisconnect;
};
void tst_QObject::connectFunctorDeadlock()
{
SenderObject sender;
MyFunctor functor(&sender);
QObject::connect(&sender, &SenderObject::signal1, functor);
sender.emitSignal1();
}
void tst_QObject::connectFunctorMoveOnly()
{
struct MoveOnlyFunctor {
Q_DISABLE_COPY(MoveOnlyFunctor)
MoveOnlyFunctor(int *status) : status(status) {}
MoveOnlyFunctor(MoveOnlyFunctor &&o) : status(o.status) { o.status = nullptr; };
void operator()(int i) { *status = i; }
void operator()() { *status = -8; }
int *status;
};
int status = 1;
SenderObject obj;
QEventLoop e;
connect(&obj, &SenderObject::signal1, MoveOnlyFunctor(&status));
QCOMPARE(status, 1);
obj.signal1();
QCOMPARE(status, -8);
connect(&obj, &SenderObject::signal7, MoveOnlyFunctor(&status));
QCOMPARE(status, -8);
obj.signal7(7888, "Hello");
QCOMPARE(status, 7888);
// With a context
status = 1;
connect(&obj, &SenderObject::signal2, this, MoveOnlyFunctor(&status));
QCOMPARE(status, 1);
obj.signal2();
QCOMPARE(status, -8);
// QueuedConnection
status = 1;
connect(&obj, &SenderObject::signal3, this, MoveOnlyFunctor(&status), Qt::QueuedConnection);
obj.signal3();
QCOMPARE(status, 1);
QCoreApplication::processEvents();
QCOMPARE(status, -8);
}
static int s_static_slot_checker = 1;
class StaticSlotChecker : public QObject
{
Q_OBJECT
public Q_SLOTS:
static void staticSlot() { s_static_slot_checker = 2; }
};
void tst_QObject::connectStaticSlotWithObject()
{
SenderObject sender;
StaticSlotChecker *receiver = new StaticSlotChecker;
QEventLoop e;
QVERIFY(connect(&sender, &SenderObject::signal1, receiver, &StaticSlotChecker::staticSlot, Qt::QueuedConnection));
connect(&sender, &SenderObject::signal1, &e, &QEventLoop::quit, Qt::QueuedConnection);
sender.emitSignal1();
QCOMPARE(s_static_slot_checker, 1);
e.exec();
QCOMPARE(s_static_slot_checker, 2);
s_static_slot_checker = 1;
connect(receiver, &QObject::destroyed, &sender, &SenderObject::signal1, Qt::QueuedConnection);
receiver->deleteLater();
QCOMPARE(s_static_slot_checker, 1);
e.exec();
QCOMPARE(s_static_slot_checker, 1);
}
struct ComplexFunctor {
ComplexFunctor(int &overload, QList<QVariant> &result) : overload(overload), result(result) {}
void operator()(int a, int b) {
overload = 1;
result << a << b;
}
void operator()(double a, double b) {
overload = 2;
result << a << b;
}
void operator()(const QString &s) {
overload = 3;
result << s;
}
void operator()(const QString &) const {
Q_ASSERT(!"Should not be called because the non-const one should");
overload = -1;
}
template<typename T1, typename T2, typename T3, typename T4>
void operator()(const T1 &t1, const T2 &t2, const T3 &t3, const T4 &t4) {
overload = 4;
result << QVariant::fromValue(t1) << QVariant::fromValue(t2) << QVariant::fromValue(t3) << QVariant::fromValue(t4);
}
int &overload;
QList<QVariant> &result;
protected:
void operator()() const {
Q_ASSERT(!"Should not be called because it is protected");
overload = -1;
}
};
struct ComplexFunctorDeriv : ComplexFunctor {
ComplexFunctorDeriv(int &overload, QList<QVariant> &result) : ComplexFunctor(overload, result) {}
void operator()() const {
overload = 10;
}
void operator()(int a, int b) {
overload = 11;
result << a << b;
}
using ComplexFunctor::operator();
private:
void operator()(int) {
Q_ASSERT(!"Should not be called because it is private");
overload = -1;
}
};
class FunctorArgDifferenceObject : public QObject
{
Q_OBJECT
signals:
void signal_ii(int,int);
void signal_iiS(int,int, const QString &);
void signal_dd(double,double);
void signal_ddS(double,double, const QString &);
void signal_S(const QString &);
void signal_SSSS(const QString &, const QString &, const QString &, const QString &);
void signal_iiSS(int, int, const QString &, const QString &);
void signal_VV(const QVariant &, const QVariant &);
};
template<typename Functor, typename Signal>
void connectFunctorOverload_impl(Signal signal, int expOverload, QList<QVariant> expResult)
{
FunctorArgDifferenceObject obj;
int overload;
QList<QVariant> result;
QVERIFY(QObject::connect(&obj, signal, Functor(overload, result)));
obj.signal_ii(1,2);
obj.signal_iiS(3,4,"5");
obj.signal_dd(6.6,7.7);
obj.signal_ddS(8.8,9.9,"10");
obj.signal_S("11");
obj.signal_SSSS("12", "13", "14", "15");
obj.signal_iiSS(16, 17, "18", "19");
obj.signal_VV(20,21);
QCOMPARE(overload, expOverload);
QCOMPARE(result, expResult);
}
void tst_QObject::connectFunctorOverloads()
{
#if defined (Q_COMPILER_VARIADIC_TEMPLATES)
connectFunctorOverload_impl<ComplexFunctor>(&FunctorArgDifferenceObject::signal_ii, 1,
(QList<QVariant>() << 1 << 2));
connectFunctorOverload_impl<ComplexFunctor>(&FunctorArgDifferenceObject::signal_iiS, 1,
(QList<QVariant>() << 3 << 4));
connectFunctorOverload_impl<ComplexFunctor>(&FunctorArgDifferenceObject::signal_dd, 2,
(QList<QVariant>() << 6.6 << 7.7));
connectFunctorOverload_impl<ComplexFunctor>(&FunctorArgDifferenceObject::signal_ddS, 2,
(QList<QVariant>() << 8.8 << 9.9));
connectFunctorOverload_impl<ComplexFunctor>(&FunctorArgDifferenceObject::signal_S, 3,
(QList<QVariant>() << QString("11")));
connectFunctorOverload_impl<ComplexFunctor>(&FunctorArgDifferenceObject::signal_SSSS, 4,
(QList<QVariant>() << QString("12") << QString("13") << QString("14") << QString("15")));
connectFunctorOverload_impl<ComplexFunctor>(&FunctorArgDifferenceObject::signal_iiSS, 4,
(QList<QVariant>() << 16 << 17 << QString("18") << QString("19")));
connectFunctorOverload_impl<ComplexFunctorDeriv>(&FunctorArgDifferenceObject::signal_ii, 11,
(QList<QVariant>() << 1 << 2));
connectFunctorOverload_impl<ComplexFunctorDeriv>(&FunctorArgDifferenceObject::signal_iiS, 11,
(QList<QVariant>() << 3 << 4));
connectFunctorOverload_impl<ComplexFunctorDeriv>(&FunctorArgDifferenceObject::signal_dd, 2,
(QList<QVariant>() << 6.6 << 7.7));
connectFunctorOverload_impl<ComplexFunctorDeriv>(&FunctorArgDifferenceObject::signal_ddS, 2,
(QList<QVariant>() << 8.8 << 9.9));
connectFunctorOverload_impl<ComplexFunctorDeriv>(&FunctorArgDifferenceObject::signal_S, 3,
(QList<QVariant>() << QString("11")));
connectFunctorOverload_impl<ComplexFunctorDeriv>(&FunctorArgDifferenceObject::signal_SSSS, 4,
(QList<QVariant>() << QString("12") << QString("13") << QString("14") << QString("15")));
connectFunctorOverload_impl<ComplexFunctorDeriv>(&FunctorArgDifferenceObject::signal_iiSS, 4,
(QList<QVariant>() << 16 << 17 << QString("18") << QString("19")));
connectFunctorOverload_impl<ComplexFunctorDeriv>(&FunctorArgDifferenceObject::signal_VV, 10,
(QList<QVariant>()));
#else
QSKIP("Does not compile without C++11 variadic template");
#endif
}
class GetSenderObject : public QObject
{
Q_OBJECT
public:
using QObject::sender; // make public
public Q_SLOTS:
void triggerSignal() { Q_EMIT aSignal(); }
Q_SIGNALS:
void aSignal();
};
static int countedStructObjectsCount = 0;
struct CountedStruct
{
CountedStruct() : sender(nullptr) { ++countedStructObjectsCount; }
CountedStruct(GetSenderObject *sender) : sender(sender) { ++countedStructObjectsCount; }
CountedStruct(const CountedStruct &o) : sender(o.sender) { ++countedStructObjectsCount; }
CountedStruct &operator=(const CountedStruct &) { return *this; }
// calling sender() here allows us to check if there's a deadlock
~CountedStruct() { --countedStructObjectsCount; if (sender) (void)sender->sender(); }
void operator()() const { }
GetSenderObject *sender;
};
void tst_QObject::disconnectDoesNotLeakFunctor()
{
QCOMPARE(countedStructObjectsCount, 0);
{
GetSenderObject obj;
QMetaObject::Connection c;
{
CountedStruct s(&obj);
QCOMPARE(countedStructObjectsCount, 1);
c = connect(&obj, &GetSenderObject::aSignal, s);
QVERIFY(c);
QCOMPARE(countedStructObjectsCount, 2);
QVERIFY(QObject::disconnect(c));
QCOMPARE(countedStructObjectsCount, 1);
}
QCOMPARE(countedStructObjectsCount, 0);
}
QCOMPARE(countedStructObjectsCount, 0);
{
GetSenderObject obj;
QMetaObject::Connection c;
{
CountedStruct s(&obj);
QObject context;
QCOMPARE(countedStructObjectsCount, 1);
c = connect(&obj, &GetSenderObject::aSignal, &context, s);
QVERIFY(c);
QCOMPARE(countedStructObjectsCount, 2);
QVERIFY(QObject::disconnect(c));
QCOMPARE(countedStructObjectsCount, 1);
}
QCOMPARE(countedStructObjectsCount, 0);
}
QCOMPARE(countedStructObjectsCount, 0);
{
QMetaObject::Connection c1, c2;
{
CountedStruct s;
QCOMPARE(countedStructObjectsCount, 1);
QTimer timer;
c1 = connect(&timer, &QTimer::timeout, s);
QVERIFY(c1);
c2 = c1;
QVERIFY(c2);
QCOMPARE(countedStructObjectsCount, 2);
QVERIFY(QObject::disconnect(c1));
QVERIFY(!c1);
QVERIFY(!c2);
// functor object has been destroyed
QCOMPARE(countedStructObjectsCount, 1);
QVERIFY(!QObject::disconnect(c2));
QCOMPARE(countedStructObjectsCount, 1);
}
QCOMPARE(countedStructObjectsCount, 0);
}
QCOMPARE(countedStructObjectsCount, 0);
{
CountedStruct s;
QCOMPARE(countedStructObjectsCount, 1);
QTimer timer;
QMetaObject::Connection c = connect(&timer, &QTimer::timeout, s);
QVERIFY(c);
QCOMPARE(countedStructObjectsCount, 2);
QVERIFY(QObject::disconnect(c));
QCOMPARE(countedStructObjectsCount, 1);
}
QCOMPARE(countedStructObjectsCount, 0);
{
QTimer timer;
QMetaObject::Connection c = connect(&timer, &QTimer::timeout, CountedStruct());
QVERIFY(c);
QCOMPARE(countedStructObjectsCount, 1); // only one instance, in Qt internals
QVERIFY(QObject::disconnect(c));
QCOMPARE(countedStructObjectsCount, 0); // functor being destroyed
}
QCOMPARE(countedStructObjectsCount, 0);
{
QTimer *timer = new QTimer;
QEventLoop e;
connect(timer, &QTimer::timeout, CountedStruct());
QCOMPARE(countedStructObjectsCount, 1); // only one instance, in Qt internals
timer->deleteLater();
connect(timer, &QObject::destroyed, &e, &QEventLoop::quit, Qt::QueuedConnection);
e.exec();
QCOMPARE(countedStructObjectsCount, 0); // functor being destroyed
}
QCOMPARE(countedStructObjectsCount, 0);
{
GetSenderObject obj;
connect(&obj, &GetSenderObject::aSignal, CountedStruct(&obj));
QCOMPARE(countedStructObjectsCount, 1);
}
QCOMPARE(countedStructObjectsCount, 0);
{
GetSenderObject obj;
connect(&obj, &GetSenderObject::aSignal, CountedStruct(&obj));
QCOMPARE(countedStructObjectsCount, 1);
QObject::disconnect(&obj, &GetSenderObject::aSignal, 0, 0);
}
QCOMPARE(countedStructObjectsCount, 0);
{
CountedStruct s;
QCOMPARE(countedStructObjectsCount, 1);
QTimer timer;
QMetaObject::Connection c = connect(&timer, &QTimer::timeout, [s](){});
QVERIFY(c);
QCOMPARE(countedStructObjectsCount, 2);
QVERIFY(QObject::disconnect(c));
QCOMPARE(countedStructObjectsCount, 1);
}
QCOMPARE(countedStructObjectsCount, 0);
}
void tst_QObject::contextDoesNotLeakFunctor()
{
QCOMPARE(countedStructObjectsCount, 0);
{
QMetaObject::Connection c;
{
QEventLoop e;
ContextObject *context = new ContextObject;
SenderObject obj;
connect(&obj, &SenderObject::signal1, context, CountedStruct());
connect(context, &QObject::destroyed, &e, &QEventLoop::quit, Qt::QueuedConnection);
context->deleteLater();
QCOMPARE(countedStructObjectsCount, 1);
e.exec();
QCOMPARE(countedStructObjectsCount, 0);
}
QCOMPARE(countedStructObjectsCount, 0);
}
QCOMPARE(countedStructObjectsCount, 0);
{
GetSenderObject obj;
QMetaObject::Connection c;
{
CountedStruct s(&obj);
QEventLoop e;
ContextObject *context = new ContextObject;
QCOMPARE(countedStructObjectsCount, 1);
connect(&obj, &GetSenderObject::aSignal, context, s);
QCOMPARE(countedStructObjectsCount, 2);
connect(context, &QObject::destroyed, &e, &QEventLoop::quit, Qt::QueuedConnection);
context->deleteLater();
e.exec();
QCOMPARE(countedStructObjectsCount, 1);
}
QCOMPARE(countedStructObjectsCount, 0);
}
QCOMPARE(countedStructObjectsCount, 0);
{
CountedStruct s;
QEventLoop e;
ContextObject *context = new ContextObject;
QCOMPARE(countedStructObjectsCount, 1);
QTimer timer;
connect(&timer, &QTimer::timeout, context, [s](){});
QCOMPARE(countedStructObjectsCount, 2);
connect(context, &QObject::destroyed, &e, &QEventLoop::quit, Qt::QueuedConnection);
context->deleteLater();
e.exec();
QCOMPARE(countedStructObjectsCount, 1);
}
QCOMPARE(countedStructObjectsCount, 0);
}
class SubSender : public SenderObject {
Q_OBJECT
};
void tst_QObject::connectBase()
{
SubSender sub;
ReceiverObject r1;
r1.reset();
QVERIFY( connect( &sub, &SubSender::signal1 , &r1, &ReceiverObject::slot1 ) );
QVERIFY( connect( &sub, static_cast<void (SenderObject::*)()>(&SubSender::signal2) , &r1, &ReceiverObject::slot2 ) );
QVERIFY( connect( &sub, static_cast<void (SubSender::*)()>(&SubSender::signal3) , &r1, &ReceiverObject::slot3 ) );
sub.emitSignal1();
sub.emitSignal2();
sub.emitSignal3();
QCOMPARE( r1.count_slot1, 1 );
QCOMPARE( r1.count_slot2, 1 );
QCOMPARE( r1.count_slot3, 1 );
QVERIFY( QObject::disconnect( &sub, &SubSender::signal1 , &r1, &ReceiverObject::slot1 ) );
QVERIFY( QObject::disconnect( &sub, static_cast<void (SenderObject::*)()>(&SubSender::signal2) , &r1, &ReceiverObject::slot2 ) );
QVERIFY( QObject::disconnect( &sub, static_cast<void (SubSender::*)()>(&SubSender::signal3) , &r1, &ReceiverObject::slot3 ) );
sub.emitSignal1();
sub.emitSignal2();
sub.emitSignal3();
QCOMPARE( r1.count_slot1, 1 );
QCOMPARE( r1.count_slot2, 1 );
QCOMPARE( r1.count_slot3, 1 );
}
void tst_QObject::connectWarnings()
{
SubSender sub;
SenderObject obj;
ReceiverObject r1;
r1.reset();
QTest::ignoreMessage(QtWarningMsg, "QObject::connect(SenderObject, ReceiverObject): invalid nullptr parameter");
connect(static_cast<const SenderObject *>(nullptr), &SubSender::signal1, &r1, &ReceiverObject::slot1);
QTest::ignoreMessage(QtWarningMsg, "QObject::connect(SubSender, Unknown): invalid nullptr parameter");
connect(&sub, &SubSender::signal1, static_cast<ReceiverObject *>(nullptr), &ReceiverObject::slot1);
QTest::ignoreMessage(QtWarningMsg, "QObject::connect(SenderObject, ReceiverObject): invalid nullptr parameter");
connect(static_cast<const SenderObject *>(nullptr), &SenderObject::signal1, &r1, &ReceiverObject::slot1);
QTest::ignoreMessage(QtWarningMsg, "QObject::connect(SenderObject, Unknown): invalid nullptr parameter");
connect(&obj, &SenderObject::signal1, static_cast<ReceiverObject *>(nullptr), &ReceiverObject::slot1);
}
struct QmlReceiver : public QtPrivate::QSlotObjectBase
{
int callCount;
void *magic;
QmlReceiver()
: QtPrivate::QSlotObjectBase(&impl)
, callCount(0)
, magic(0)
{}
static void impl(int which, QSlotObjectBase *this_, QObject *, void **metaArgs, bool *ret)
{
switch (which) {
case Destroy: delete static_cast<QmlReceiver*>(this_); return;
case Call: static_cast<QmlReceiver*>(this_)->callCount++; return;
case Compare: *ret = static_cast<QmlReceiver*>(this_)->magic == metaArgs[0]; return;
case NumOperations: break;
}
}
};
void tst_QObject::qmlConnect()
{
#ifdef QT_BUILD_INTERNAL
SenderObject sender;
QmlReceiver *receiver = new QmlReceiver;
receiver->magic = receiver;
receiver->ref();
QVERIFY(QObjectPrivate::connect(&sender, sender.metaObject()->indexOfSignal("signal1()"),
receiver, Qt::AutoConnection));
QCOMPARE(receiver->callCount, 0);
sender.emitSignal1();
QCOMPARE(receiver->callCount, 1);
void *a[] = {
receiver
};
QVERIFY(QObjectPrivate::disconnect(&sender, sender.metaObject()->indexOfSignal("signal1()"), reinterpret_cast<void**>(&a)));
sender.emitSignal1();
QCOMPARE(receiver->callCount, 1);
receiver->destroyIfLastRef();
#else
QSKIP("Needs QT_BUILD_INTERNAL");
#endif
}
#ifndef QT_NO_EXCEPTIONS
class ObjectException : public std::exception { };
struct ThrowFunctor
{
CountedStruct operator()(const CountedStruct &, CountedStruct s2) const
{
throw ObjectException();
return s2;
}
CountedStruct s;
};
#endif
class ExceptionThrower : public QObject
{
Q_OBJECT
public slots:
CountedStruct throwException(const CountedStruct &, CountedStruct s2)
{
#ifndef QT_NO_EXCEPTIONS
throw ObjectException();
#endif
return s2;
}
signals:
CountedStruct mySignal(const CountedStruct &s1, CountedStruct s2);
};
class CountedExceptionThrower : public QObject
{
Q_OBJECT
public:
explicit CountedExceptionThrower(bool throwException, QObject *parent = nullptr)
: QObject(parent)
{
if (throwException)
throw ObjectException();
++counter;
}
~CountedExceptionThrower()
{
--counter;
}
static int counter;
};
int CountedExceptionThrower::counter = 0;
void tst_QObject::exceptions()
{
#ifndef QT_NO_EXCEPTIONS
ReceiverObject receiver;
// String based syntax
{
QCOMPARE(countedStructObjectsCount, 0);
ExceptionThrower thrower;
receiver.reset();
connect(&thrower, SIGNAL(mySignal(CountedStruct,CountedStruct)), &receiver, SLOT(slot1()));
connect(&thrower, SIGNAL(mySignal(CountedStruct,CountedStruct)), &thrower, SLOT(throwException(CountedStruct,CountedStruct)));
connect(&thrower, SIGNAL(mySignal(CountedStruct,CountedStruct)), &receiver, SLOT(slot2()));
try {
CountedStruct s;
emit thrower.mySignal(s, s);
QFAIL("Exception not thrown?");
} catch (ObjectException&) {}
QCOMPARE(receiver.count_slot1, 1);
QCOMPARE(receiver.count_slot2, 0);
QCOMPARE(countedStructObjectsCount, 0);
}
// Pointer to member function
{
QCOMPARE(countedStructObjectsCount, 0);
ExceptionThrower thrower;
receiver.reset();
connect(&thrower, &ExceptionThrower::mySignal, &receiver, &ReceiverObject::slot1);
connect(&thrower, &ExceptionThrower::mySignal, &thrower, &ExceptionThrower::throwException);
connect(&thrower, &ExceptionThrower::mySignal, &receiver, &ReceiverObject::slot2);
try {
CountedStruct s;
emit thrower.mySignal(s, s);
QFAIL("Exception not thrown?");
} catch (ObjectException&) {}
QCOMPARE(receiver.count_slot1, 1);
QCOMPARE(receiver.count_slot2, 0);
QCOMPARE(countedStructObjectsCount, 0);
}
// Functor
{
QCOMPARE(countedStructObjectsCount, 0);
ExceptionThrower thrower;
receiver.reset();
connect(&thrower, &ExceptionThrower::mySignal, &receiver, &ReceiverObject::slot1);
connect(&thrower, &ExceptionThrower::mySignal, ThrowFunctor());
connect(&thrower, &ExceptionThrower::mySignal, &receiver, &ReceiverObject::slot2);
try {
CountedStruct s;
emit thrower.mySignal(s, s);
QFAIL("Exception not thrown?");
} catch (ObjectException&) {}
QCOMPARE(receiver.count_slot1, 1);
QCOMPARE(receiver.count_slot2, 0);
QCOMPARE(countedStructObjectsCount, 1); // the Functor
}
QCOMPARE(countedStructObjectsCount, 0);
// Child object reaping in case of exceptions thrown by constructors
{
QCOMPARE(CountedExceptionThrower::counter, 0);
try {
class ParentObject : public QObject {
public:
explicit ParentObject(QObject *parent = nullptr)
: QObject(parent)
{
new CountedExceptionThrower(false, this);
new CountedExceptionThrower(false, this);
new CountedExceptionThrower(true, this); // throws
}
};
ParentObject p;
QFAIL("Exception not thrown");
} catch (const ObjectException &) {
} catch (...) {
QFAIL("Wrong exception thrown");
}
QCOMPARE(CountedExceptionThrower::counter, 0);
try {
QObject o;
new CountedExceptionThrower(false, &o);
new CountedExceptionThrower(false, &o);
new CountedExceptionThrower(true, &o); // throws
QFAIL("Exception not thrown");
} catch (const ObjectException &) {
} catch (...) {
QFAIL("Wrong exception thrown");
}
QCOMPARE(CountedExceptionThrower::counter, 0);
try {
QObject o;
CountedExceptionThrower c1(false, &o);
CountedExceptionThrower c2(false, &o);
CountedExceptionThrower c3(true, &o); // throws
QFAIL("Exception not thrown");
} catch (const ObjectException &) {
} catch (...) {
QFAIL("Wrong exception thrown");
}
QCOMPARE(CountedExceptionThrower::counter, 0);
}
#else
QSKIP("Needs exceptions");
#endif
}
#ifdef QT_BUILD_INTERNAL
static bool parentChangeCalled = false;
static void testParentChanged(QAbstractDeclarativeData *, QObject *, QObject *)
{
parentChangeCalled = true;
}
#endif
void tst_QObject::noDeclarativeParentChangedOnDestruction()
{
#ifdef QT_BUILD_INTERNAL
typedef void (*ParentChangedCallback)(QAbstractDeclarativeData *, QObject *, QObject *);
QScopedValueRollback<ParentChangedCallback> rollback(QAbstractDeclarativeData::parentChanged);
QAbstractDeclarativeData::parentChanged = testParentChanged;
QObject *parent = new QObject;
QObject *child = new QObject;
QAbstractDeclarativeDataImpl dummy;
dummy.ownedByQml1 = false;
QObjectPrivate::get(child)->declarativeData = &dummy;
parentChangeCalled = false;
child->setParent(parent);
QVERIFY(parentChangeCalled);
parentChangeCalled = false;
delete child;
QVERIFY(!parentChangeCalled);
delete parent;
#else
QSKIP("Needs QT_BUILD_INTERNAL");
#endif
}
struct MutableFunctor {
int count;
MutableFunctor() : count(0) {}
int operator()() { return ++count; }
};
void tst_QObject::mutableFunctor()
{
ReturnValue o;
MutableFunctor functor;
QCOMPARE(functor.count, 0);
connect(&o, &ReturnValue::returnInt, functor);
QCOMPARE(emit o.returnInt(0), 1);
QCOMPARE(emit o.returnInt(0), 2); // each emit should increase the internal count
QCOMPARE(functor.count, 0); // but the original object should have been copied at connect time
}
void tst_QObject::checkArgumentsForNarrowing()
{
enum UnscopedEnum {};
enum SignedUnscopedEnum { SignedUnscopedEnumV1 = -1, SignedUnscopedEnumV2 = 1 };
// a constexpr would suffice, but MSVC2013 RTM doesn't support them...
#define IS_UNSCOPED_ENUM_SIGNED (std::is_signed<typename std::underlying_type<UnscopedEnum>::type>::value)
#define NARROWS_IF(x, y, test) Q_STATIC_ASSERT((QtPrivate::AreArgumentsNarrowedBase<x, y>::value) == (test))
#define FITS_IF(x, y, test) Q_STATIC_ASSERT((QtPrivate::AreArgumentsNarrowedBase<x, y>::value) != (test))
#define NARROWS(x, y) NARROWS_IF(x, y, true)
#define FITS(x, y) FITS_IF(x, y, true)
Q_STATIC_ASSERT(sizeof(UnscopedEnum) <= sizeof(int));
Q_STATIC_ASSERT(sizeof(SignedUnscopedEnum) <= sizeof(int));
// floating point to integral
NARROWS(float, bool);
NARROWS(double, bool);
NARROWS(long double, bool);
NARROWS(float, char);
NARROWS(double, char);
NARROWS(long double, char);
NARROWS(float, short);
NARROWS(double, short);
NARROWS(long double, short);
NARROWS(float, int);
NARROWS(double, int);
NARROWS(long double, int);
NARROWS(float, long);
NARROWS(double, long);
NARROWS(long double, long);
NARROWS(float, long long);
NARROWS(double, long long);
NARROWS(long double, long long);
// floating point to a smaller floating point
NARROWS_IF(double, float, (sizeof(double) > sizeof(float)));
NARROWS_IF(long double, float, (sizeof(long double) > sizeof(float)));
FITS(float, double);
FITS(float, long double);
NARROWS_IF(long double, double, (sizeof(long double) > sizeof(double)));
FITS(double, long double);
// integral to floating point
NARROWS(bool, float);
NARROWS(bool, double);
NARROWS(bool, long double);
NARROWS(char, float);
NARROWS(char, double);
NARROWS(char, long double);
NARROWS(short, float);
NARROWS(short, double);
NARROWS(short, long double);
NARROWS(int, float);
NARROWS(int, double);
NARROWS(int, long double);
NARROWS(long, float);
NARROWS(long, double);
NARROWS(long, long double);
NARROWS(long long, float);
NARROWS(long long, double);
NARROWS(long long, long double);
// enum to floating point
NARROWS(UnscopedEnum, float);
NARROWS(UnscopedEnum, double);
NARROWS(UnscopedEnum, long double);
NARROWS(SignedUnscopedEnum, float);
NARROWS(SignedUnscopedEnum, double);
NARROWS(SignedUnscopedEnum, long double);
// integral to smaller integral
FITS(bool, bool);
FITS(char, char);
FITS(signed char, signed char);
FITS(signed char, short);
FITS(signed char, int);
FITS(signed char, long);
FITS(signed char, long long);
FITS(unsigned char, unsigned char);
FITS(unsigned char, unsigned short);
FITS(unsigned char, unsigned int);
FITS(unsigned char, unsigned long);
FITS(unsigned char, unsigned long long);
NARROWS_IF(bool, unsigned char, (sizeof(bool) > sizeof(char) || std::is_signed<bool>::value));
NARROWS_IF(bool, unsigned short, (sizeof(bool) > sizeof(short) || std::is_signed<bool>::value));
NARROWS_IF(bool, unsigned int, (sizeof(bool) > sizeof(int) || std::is_signed<bool>::value));
NARROWS_IF(bool, unsigned long, (sizeof(bool) > sizeof(long) || std::is_signed<bool>::value));
NARROWS_IF(bool, unsigned long long, (sizeof(bool) > sizeof(long long) || std::is_signed<bool>::value));
NARROWS_IF(short, char, (sizeof(short) > sizeof(char) || std::is_unsigned<char>::value));
NARROWS_IF(short, unsigned char, (sizeof(short) > sizeof(char)));
NARROWS_IF(short, signed char, (sizeof(short) > sizeof(char)));
NARROWS_IF(unsigned short, char, (sizeof(short) > sizeof(char) || std::is_signed<char>::value));
NARROWS_IF(unsigned short, unsigned char, (sizeof(short) > sizeof(char)));
NARROWS_IF(unsigned short, signed char, (sizeof(short) > sizeof(char)));
FITS(short, short);
FITS(short, int);
FITS(short, long);
FITS(short, long long);
FITS(unsigned short, unsigned short);
FITS(unsigned short, unsigned int);
FITS(unsigned short, unsigned long);
FITS(unsigned short, unsigned long long);
NARROWS_IF(int, char, (sizeof(int) > sizeof(char) || std::is_unsigned<char>::value));
NARROWS(int, unsigned char);
NARROWS_IF(int, signed char, (sizeof(int) > sizeof(char)));
NARROWS_IF(int, short, (sizeof(int) > sizeof(short)));
NARROWS(int, unsigned short);
NARROWS_IF(unsigned int, char, (sizeof(int) > sizeof(char) || std::is_signed<char>::value));
NARROWS_IF(unsigned int, unsigned char, (sizeof(int) > sizeof(char)));
NARROWS(unsigned int, signed char);
NARROWS(unsigned int, short);
NARROWS_IF(unsigned int, unsigned short, (sizeof(int) > sizeof(short)));
FITS(int, int);
FITS(int, long);
FITS(int, long long);
FITS(unsigned int, unsigned int);
FITS(unsigned int, unsigned long);
FITS(unsigned int, unsigned long long);
NARROWS_IF(long, char, (sizeof(long) > sizeof(char) || std::is_unsigned<char>::value));
NARROWS(long, unsigned char);
NARROWS_IF(long, signed char, (sizeof(long) > sizeof(char)));
NARROWS_IF(long, short, (sizeof(long) > sizeof(short)));
NARROWS(long, unsigned short);
NARROWS_IF(long, int, (sizeof(long) > sizeof(int)));
NARROWS(long, unsigned int);
NARROWS_IF(unsigned long, char, (sizeof(long) > sizeof(char) || std::is_signed<char>::value));
NARROWS_IF(unsigned long, unsigned char, (sizeof(long) > sizeof(char)));
NARROWS(unsigned long, signed char);
NARROWS(unsigned long, short);
NARROWS_IF(unsigned long, unsigned short, (sizeof(long) > sizeof(short)));
NARROWS(unsigned long, int);
NARROWS_IF(unsigned long, unsigned int, (sizeof(long) > sizeof(int)));
FITS(long, long);
FITS(long, long long);
FITS(unsigned long, unsigned long);
FITS(unsigned long, unsigned long long);
NARROWS_IF(long long, char, (sizeof(long long) > sizeof(char) || std::is_unsigned<char>::value));
NARROWS(long long, unsigned char);
NARROWS_IF(long long, signed char, (sizeof(long long) > sizeof(char)));
NARROWS_IF(long long, short, (sizeof(long long) > sizeof(short)));
NARROWS(long long, unsigned short);
NARROWS_IF(long long, int, (sizeof(long long) > sizeof(int)));
NARROWS(long long, unsigned int);
NARROWS_IF(long long, long, (sizeof(long long) > sizeof(long)));
NARROWS(long long, unsigned long);
NARROWS_IF(unsigned long long, char, (sizeof(long long) > sizeof(char) || std::is_signed<char>::value));
NARROWS_IF(unsigned long long, unsigned char, (sizeof(long long) > sizeof(char)));
NARROWS(unsigned long long, signed char);
NARROWS(unsigned long long, short);
NARROWS_IF(unsigned long long, unsigned short, (sizeof(long long) > sizeof(short)));
NARROWS(unsigned long long, int);
NARROWS_IF(unsigned long long, unsigned int, (sizeof(long long) > sizeof(int)));
NARROWS(unsigned long long, long);
NARROWS_IF(unsigned long long, unsigned long, (sizeof(long long) > sizeof(long)));
FITS(long long, long long);
FITS(unsigned long long, unsigned long long);
// integral to integral with different signedness. smaller ones tested above
NARROWS(signed char, unsigned char);
NARROWS(signed char, unsigned short);
NARROWS(signed char, unsigned int);
NARROWS(signed char, unsigned long);
NARROWS(signed char, unsigned long long);
NARROWS(unsigned char, signed char);
FITS(unsigned char, short);
FITS(unsigned char, int);
FITS(unsigned char, long);
FITS(unsigned char, long long);
NARROWS(short, unsigned short);
NARROWS(short, unsigned int);
NARROWS(short, unsigned long);
NARROWS(short, unsigned long long);
NARROWS(unsigned short, short);
FITS(unsigned short, int);
FITS(unsigned short, long);
FITS(unsigned short, long long);
NARROWS(int, unsigned int);
NARROWS(int, unsigned long);
NARROWS(int, unsigned long long);
NARROWS(unsigned int, int);
NARROWS_IF(unsigned int, long, (sizeof(int) >= sizeof(long)));
FITS(unsigned int, long long);
NARROWS(long, unsigned long);
NARROWS(long, unsigned long long);
NARROWS(unsigned long, long);
NARROWS_IF(unsigned long, long long, (sizeof(long) >= sizeof(long long)));
NARROWS(long long, unsigned long long);
NARROWS(unsigned long long, long long);
// enum to smaller integral
// (note that we know that sizeof(UnscopedEnum) <= sizeof(int)
FITS(UnscopedEnum, UnscopedEnum);
FITS(SignedUnscopedEnum, SignedUnscopedEnum);
NARROWS_IF(UnscopedEnum, char, ((sizeof(UnscopedEnum) > sizeof(char)) || (sizeof(UnscopedEnum) == sizeof(char) && IS_UNSCOPED_ENUM_SIGNED == std::is_signed<char>::value)));
NARROWS_IF(UnscopedEnum, signed char, ((sizeof(UnscopedEnum) > sizeof(char)) || (sizeof(UnscopedEnum) == sizeof(char) && !IS_UNSCOPED_ENUM_SIGNED)));
NARROWS_IF(UnscopedEnum, unsigned char, ((sizeof(UnscopedEnum) > sizeof(char)) || IS_UNSCOPED_ENUM_SIGNED));
NARROWS_IF(UnscopedEnum, short, ((sizeof(UnscopedEnum) > sizeof(short)) || (sizeof(UnscopedEnum) == sizeof(short) && !IS_UNSCOPED_ENUM_SIGNED)));
NARROWS_IF(UnscopedEnum, unsigned short, ((sizeof(UnscopedEnum) > sizeof(short)) || IS_UNSCOPED_ENUM_SIGNED));
NARROWS_IF(UnscopedEnum, int, (sizeof(UnscopedEnum) == sizeof(int) && !IS_UNSCOPED_ENUM_SIGNED));
NARROWS_IF(UnscopedEnum, unsigned int, IS_UNSCOPED_ENUM_SIGNED);
NARROWS_IF(UnscopedEnum, long, (sizeof(UnscopedEnum) == sizeof(long) && !IS_UNSCOPED_ENUM_SIGNED));
NARROWS_IF(UnscopedEnum, unsigned long, IS_UNSCOPED_ENUM_SIGNED);
NARROWS_IF(UnscopedEnum, long long, (sizeof(UnscopedEnum) == sizeof(long long) && !IS_UNSCOPED_ENUM_SIGNED));
NARROWS_IF(UnscopedEnum, unsigned long long, IS_UNSCOPED_ENUM_SIGNED);
Q_STATIC_ASSERT(std::is_signed<typename std::underlying_type<SignedUnscopedEnum>::type>::value);
NARROWS_IF(SignedUnscopedEnum, signed char, (sizeof(SignedUnscopedEnum) > sizeof(char)));
NARROWS_IF(SignedUnscopedEnum, short, (sizeof(SignedUnscopedEnum) > sizeof(short)));
FITS(SignedUnscopedEnum, int);
FITS(SignedUnscopedEnum, long);
FITS(SignedUnscopedEnum, long long);
enum class ScopedEnumBackedBySChar : signed char { A };
enum class ScopedEnumBackedByUChar : unsigned char { A };
enum class ScopedEnumBackedByShort : short { A };
enum class ScopedEnumBackedByUShort : unsigned short { A };
enum class ScopedEnumBackedByInt : int { A };
enum class ScopedEnumBackedByUInt : unsigned int { A };
enum class ScopedEnumBackedByLong : long { A };
enum class ScopedEnumBackedByULong : unsigned long { A };
enum class ScopedEnumBackedByLongLong : long long { A };
enum class ScopedEnumBackedByULongLong : unsigned long long { A };
FITS(ScopedEnumBackedBySChar, ScopedEnumBackedBySChar);
FITS(ScopedEnumBackedByUChar, ScopedEnumBackedByUChar);
FITS(ScopedEnumBackedByShort, ScopedEnumBackedByShort);
FITS(ScopedEnumBackedByUShort, ScopedEnumBackedByUShort);
FITS(ScopedEnumBackedByInt, ScopedEnumBackedByInt);
FITS(ScopedEnumBackedByUInt, ScopedEnumBackedByUInt);
FITS(ScopedEnumBackedByLong, ScopedEnumBackedByLong);
FITS(ScopedEnumBackedByULong, ScopedEnumBackedByULong);
FITS(ScopedEnumBackedByLongLong, ScopedEnumBackedByLongLong);
FITS(ScopedEnumBackedByULongLong, ScopedEnumBackedByULongLong);
FITS(ScopedEnumBackedBySChar, signed char);
FITS(ScopedEnumBackedByUChar, unsigned char);
FITS(ScopedEnumBackedByShort, short);
FITS(ScopedEnumBackedByUShort, unsigned short);
FITS(ScopedEnumBackedByInt, int);
FITS(ScopedEnumBackedByUInt, unsigned int);
FITS(ScopedEnumBackedByLong, long);
FITS(ScopedEnumBackedByULong, unsigned long);
FITS(ScopedEnumBackedByLongLong, long long);
FITS(ScopedEnumBackedByULongLong, unsigned long long);
FITS(ScopedEnumBackedBySChar, signed char);
FITS(ScopedEnumBackedBySChar, short);
FITS(ScopedEnumBackedBySChar, int);
FITS(ScopedEnumBackedBySChar, long);
FITS(ScopedEnumBackedBySChar, long long);
FITS(ScopedEnumBackedByUChar, unsigned char);
FITS(ScopedEnumBackedByUChar, unsigned short);
FITS(ScopedEnumBackedByUChar, unsigned int);
FITS(ScopedEnumBackedByUChar, unsigned long);
FITS(ScopedEnumBackedByUChar, unsigned long long);
NARROWS_IF(ScopedEnumBackedByShort, char, (sizeof(short) > sizeof(char) || std::is_unsigned<char>::value));
NARROWS_IF(ScopedEnumBackedByUShort, char, (sizeof(short) > sizeof(char) || std::is_signed<char>::value));
NARROWS_IF(ScopedEnumBackedByInt, char, (sizeof(int) > sizeof(char) || std::is_unsigned<char>::value));
NARROWS_IF(ScopedEnumBackedByUInt, char, (sizeof(int) > sizeof(char) || std::is_signed<char>::value));
NARROWS_IF(ScopedEnumBackedByLong, char, (sizeof(long) > sizeof(char) || std::is_unsigned<char>::value));
NARROWS_IF(ScopedEnumBackedByULong, char, (sizeof(long) > sizeof(char) || std::is_signed<char>::value));
NARROWS_IF(ScopedEnumBackedByLongLong, char, (sizeof(long long) > sizeof(char) || std::is_unsigned<char>::value));
NARROWS_IF(ScopedEnumBackedByULongLong, char, (sizeof(long long) > sizeof(char) || std::is_signed<char>::value));
NARROWS_IF(ScopedEnumBackedByShort, signed char, (sizeof(short) > sizeof(char)));
NARROWS(ScopedEnumBackedByUShort, signed char);
NARROWS_IF(ScopedEnumBackedByInt, signed char, (sizeof(int) > sizeof(char)));
NARROWS(ScopedEnumBackedByUInt, signed char);
NARROWS_IF(ScopedEnumBackedByLong, signed char, (sizeof(long) > sizeof(char)));
NARROWS(ScopedEnumBackedByULong, signed char);
NARROWS_IF(ScopedEnumBackedByLongLong, signed char, (sizeof(long long) > sizeof(char)));
NARROWS(ScopedEnumBackedByULongLong, signed char);
NARROWS(ScopedEnumBackedByShort, unsigned char);
NARROWS_IF(ScopedEnumBackedByUShort, unsigned char, (sizeof(short) > sizeof(char)));
NARROWS(ScopedEnumBackedByInt, unsigned char);
NARROWS_IF(ScopedEnumBackedByUInt, unsigned char, (sizeof(int) > sizeof(char)));
NARROWS(ScopedEnumBackedByLong, unsigned char);
NARROWS_IF(ScopedEnumBackedByULong, unsigned char, (sizeof(long) > sizeof(char)));
NARROWS(ScopedEnumBackedByLongLong, unsigned char);
NARROWS_IF(ScopedEnumBackedByULongLong, unsigned char, (sizeof(long long) > sizeof(char)));
NARROWS_IF(ScopedEnumBackedByInt, short, (sizeof(int) > sizeof(short)));
NARROWS(ScopedEnumBackedByUInt, short);
NARROWS_IF(ScopedEnumBackedByLong, short, (sizeof(long) > sizeof(short)));
NARROWS(ScopedEnumBackedByULong, short);
NARROWS_IF(ScopedEnumBackedByLongLong, short, (sizeof(long long) > sizeof(short)));
NARROWS(ScopedEnumBackedByULongLong, short);
NARROWS(ScopedEnumBackedByInt, unsigned short);
NARROWS_IF(ScopedEnumBackedByUInt, unsigned short, (sizeof(int) > sizeof(short)));
NARROWS(ScopedEnumBackedByLong, unsigned short);
NARROWS_IF(ScopedEnumBackedByULong, unsigned short, (sizeof(long) > sizeof(short)));
NARROWS(ScopedEnumBackedByLongLong, unsigned short);
NARROWS_IF(ScopedEnumBackedByULongLong, unsigned short, (sizeof(long long) > sizeof(short)));
NARROWS_IF(ScopedEnumBackedByLong, int, (sizeof(long) > sizeof(int)));
NARROWS(ScopedEnumBackedByULong, int);
NARROWS_IF(ScopedEnumBackedByLongLong, int, (sizeof(long long) > sizeof(int)));
NARROWS(ScopedEnumBackedByULongLong, int);
NARROWS(ScopedEnumBackedByLong, unsigned int);
NARROWS_IF(ScopedEnumBackedByULong, unsigned int, (sizeof(long) > sizeof(int)));
NARROWS(ScopedEnumBackedByLongLong, unsigned int);
NARROWS_IF(ScopedEnumBackedByULongLong, unsigned int, (sizeof(long long) > sizeof(int)));
NARROWS_IF(ScopedEnumBackedByLongLong, long, (sizeof(long long) > sizeof(long)));
NARROWS(ScopedEnumBackedByULongLong, long);
NARROWS(ScopedEnumBackedByLongLong, unsigned long);
NARROWS_IF(ScopedEnumBackedByULongLong, unsigned long, (sizeof(long long) > sizeof(long)));
// different signedness of the underlying type
NARROWS(SignedUnscopedEnum, unsigned char);
NARROWS(SignedUnscopedEnum, unsigned short);
NARROWS(SignedUnscopedEnum, unsigned int);
NARROWS(SignedUnscopedEnum, unsigned long);
NARROWS(SignedUnscopedEnum, unsigned long long);
NARROWS(ScopedEnumBackedBySChar, unsigned char);
NARROWS(ScopedEnumBackedBySChar, unsigned short);
NARROWS(ScopedEnumBackedBySChar, unsigned int);
NARROWS(ScopedEnumBackedBySChar, unsigned long);
NARROWS(ScopedEnumBackedBySChar, unsigned long long);
NARROWS(ScopedEnumBackedByShort, unsigned char);
NARROWS(ScopedEnumBackedByShort, unsigned short);
NARROWS(ScopedEnumBackedByShort, unsigned int);
NARROWS(ScopedEnumBackedByShort, unsigned long);
NARROWS(ScopedEnumBackedByShort, unsigned long long);
NARROWS(ScopedEnumBackedByInt, unsigned char);
NARROWS(ScopedEnumBackedByInt, unsigned short);
NARROWS(ScopedEnumBackedByInt, unsigned int);
NARROWS(ScopedEnumBackedByInt, unsigned long);
NARROWS(ScopedEnumBackedByInt, unsigned long long);
NARROWS(ScopedEnumBackedByLong, unsigned char);
NARROWS(ScopedEnumBackedByLong, unsigned short);
NARROWS(ScopedEnumBackedByLong, unsigned int);
NARROWS(ScopedEnumBackedByLong, unsigned long);
NARROWS(ScopedEnumBackedByLong, unsigned long long);
NARROWS(ScopedEnumBackedByLongLong, unsigned char);
NARROWS(ScopedEnumBackedByLongLong, unsigned short);
NARROWS(ScopedEnumBackedByLongLong, unsigned int);
NARROWS(ScopedEnumBackedByLongLong, unsigned long);
NARROWS(ScopedEnumBackedByLongLong, unsigned long long);
NARROWS(ScopedEnumBackedByUChar, signed char);
FITS_IF(ScopedEnumBackedByUChar, short, (sizeof(char) < sizeof(short)));
FITS_IF(ScopedEnumBackedByUChar, int, (sizeof(char) < sizeof(int)));
FITS_IF(ScopedEnumBackedByUChar, long, (sizeof(char) < sizeof(long)));
FITS_IF(ScopedEnumBackedByUChar, long long, (sizeof(char) < sizeof(long long)));
NARROWS(ScopedEnumBackedByUShort, signed char);
NARROWS(ScopedEnumBackedByUShort, short);
FITS_IF(ScopedEnumBackedByUShort, int, (sizeof(short) < sizeof(int)));
FITS_IF(ScopedEnumBackedByUShort, long, (sizeof(short) < sizeof(long)));
FITS_IF(ScopedEnumBackedByUShort, long long, (sizeof(short) < sizeof(long long)));
NARROWS(ScopedEnumBackedByUInt, signed char);
NARROWS(ScopedEnumBackedByUInt, short);
NARROWS(ScopedEnumBackedByUInt, int);
FITS_IF(ScopedEnumBackedByUInt, long, (sizeof(ScopedEnumBackedByUInt) < sizeof(long)));
FITS(ScopedEnumBackedByUInt, long long);
NARROWS(ScopedEnumBackedByULong, signed char);
NARROWS(ScopedEnumBackedByULong, short);
NARROWS(ScopedEnumBackedByULong, int);
NARROWS(ScopedEnumBackedByULong, long);
FITS_IF(ScopedEnumBackedByULong, long long, (sizeof(ScopedEnumBackedByULong) < sizeof(long long)));
NARROWS(ScopedEnumBackedByULongLong, signed char);
NARROWS(ScopedEnumBackedByULongLong, short);
NARROWS(ScopedEnumBackedByULongLong, int);
NARROWS(ScopedEnumBackedByULongLong, long);
NARROWS(ScopedEnumBackedByULongLong, long long);
// other types which should be always unaffected
FITS(void *, void *);
FITS(QString, QString);
FITS(QString &, QString &);
FITS(const QString &, const QString &);
FITS(QObject, QObject);
FITS(QObject *, QObject *);
FITS(const QObject *, const QObject *);
FITS(std::nullptr_t, std::nullptr_t);
FITS(QString, QObject);
FITS(QString, QVariant);
FITS(QString, void *);
FITS(QString, long long);
FITS(bool, const QObject *&);
FITS(int (*)(bool), void (QObject::*)());
{
// wg21.link/P1957
NARROWS(char*, bool);
NARROWS(void (QObject::*)(), bool);
}
#undef IS_UNSCOPED_ENUM_SIGNED
#undef NARROWS_IF
#undef FITS_IF
#undef NARROWS
#undef FITS
}
void tst_QObject::nullReceiver()
{
QObject o;
QObject *nullObj = nullptr; // Passing nullptr directly doesn't compile with gcc 4.8
QVERIFY(!connect(&o, &QObject::destroyed, nullObj, &QObject::deleteLater));
QVERIFY(!connect(&o, &QObject::destroyed, nullObj, [] {}));
QVERIFY(!connect(&o, &QObject::destroyed, nullObj, Functor_noexcept()));
QVERIFY(!connect(&o, SIGNAL(destroyed()), nullObj, SLOT(deleteLater())));
}
void tst_QObject::functorReferencesConnection()
{
countedStructObjectsCount = 0;
QMetaObject::Connection globalCon;
{
GetSenderObject obj;
CountedStruct counted(&obj);
QCOMPARE(countedStructObjectsCount, 1);
auto c = QSharedPointer<QMetaObject::Connection>::create();
int slotCalled = 0;
*c = connect(&obj, &GetSenderObject::aSignal, &obj, [&slotCalled, c, counted] {
QObject::disconnect(*c);
slotCalled++;
});
globalCon = *c; // keep a handle to the connection somewhere;
QVERIFY(globalCon);
QCOMPARE(countedStructObjectsCount, 2);
obj.triggerSignal();
QCOMPARE(slotCalled, 1);
QCOMPARE(countedStructObjectsCount, 1);
QVERIFY(!globalCon);
obj.triggerSignal();
QCOMPARE(slotCalled, 1);
QCOMPARE(countedStructObjectsCount, 1);
}
QCOMPARE(countedStructObjectsCount, 0);
{
GetSenderObject obj;
CountedStruct counted(&obj);
QCOMPARE(countedStructObjectsCount, 1);
auto *rec = new QObject;
int slotCalled = 0;
globalCon = connect(&obj, &GetSenderObject::aSignal, rec, [&slotCalled, rec, counted] {
delete rec;
slotCalled++;
});
QCOMPARE(countedStructObjectsCount, 2);
obj.triggerSignal();
QCOMPARE(slotCalled, 1);
QCOMPARE(countedStructObjectsCount, 1);
QVERIFY(!globalCon);
obj.triggerSignal();
QCOMPARE(slotCalled, 1);
QCOMPARE(countedStructObjectsCount, 1);
}
QCOMPARE(countedStructObjectsCount, 0);
{
int slotCalled = 0;
QEventLoop eventLoop;
{
// Sender will be destroyed when the labda goes out of scope lambda, so it will exit the event loop
auto sender = QSharedPointer<GetSenderObject>::create();
connect(sender.data(), &QObject::destroyed, &eventLoop, &QEventLoop::quit, Qt::QueuedConnection);
globalCon = connect(sender.data(), &GetSenderObject::aSignal, this, [&slotCalled, sender, &globalCon, this] {
++slotCalled;
// This signal will be connected, but should never be called as the sender will be destroyed before
auto c2 = connect(sender.data(), &GetSenderObject::aSignal, [] { QFAIL("Should not be called"); });
QVERIFY(c2);
QVERIFY(QObject::disconnect(sender.data(), nullptr, this, nullptr));
QVERIFY(!globalCon); // this connection has been disconnected
QVERIFY(c2); // sender should not have been deleted yet, only after the emission is done
});
QMetaObject::invokeMethod(sender.data(), &GetSenderObject::triggerSignal, Qt::QueuedConnection);
QMetaObject::invokeMethod(sender.data(), &GetSenderObject::triggerSignal, Qt::QueuedConnection);
QMetaObject::invokeMethod(sender.data(), &GetSenderObject::triggerSignal, Qt::QueuedConnection);
}
eventLoop.exec();
QCOMPARE(slotCalled, 1);
}
{
GetSenderObject obj;
CountedStruct counted(&obj);
QCOMPARE(countedStructObjectsCount, 1);
auto c1 = QSharedPointer<QMetaObject::Connection>::create();
auto c2 = QSharedPointer<QMetaObject::Connection>::create();
int slot1Called = 0;
int slot3Called = 0;
*c1 = connect(&obj, &GetSenderObject::aSignal, &obj, [&slot1Called, &slot3Called, &obj, c1, c2, counted] {
auto c3 = connect(&obj, &GetSenderObject::aSignal, [counted, &slot3Called] {
slot3Called++;
});
// top-level + the one in the 3 others lambdas
QCOMPARE(countedStructObjectsCount, 4);
QObject::disconnect(*c2);
slot1Called++;
});
connect(&obj, &GetSenderObject::aSignal, [] {}); // just a dummy signal to fill the connection list
*c2 = connect(&obj, &GetSenderObject::aSignal, [counted, c2] { QFAIL("should not be called"); });
QVERIFY(c1 && c2);
QCOMPARE(countedStructObjectsCount, 3); // top-level + c1 + c2
obj.triggerSignal();
QCOMPARE(slot1Called, 1);
QCOMPARE(slot3Called, 0);
QCOMPARE(countedStructObjectsCount, 3); // top-level + c1 + c3
QObject::disconnect(*c1);
QCOMPARE(countedStructObjectsCount, 2); // top-level + c3
obj.triggerSignal();
QCOMPARE(slot1Called, 1);
QCOMPARE(slot3Called, 1);
}
{
struct DestroyEmit {
Q_DISABLE_COPY(DestroyEmit);
explicit DestroyEmit(SenderObject *obj) : obj(obj) {}
SenderObject *obj;
~DestroyEmit() {
obj->emitSignal1();
}
};
SenderObject obj;
int slot1Called = 0;
int slot2Called = 0;
int slot3Called = 0;
auto c1 = QSharedPointer<QMetaObject::Connection>::create();
auto de = QSharedPointer<DestroyEmit>::create(&obj);
*c1 = connect(&obj, &SenderObject::signal1, [&slot1Called, &slot3Called, de, c1, &obj] {
connect(&obj, &SenderObject::signal1, [&slot3Called] { slot3Called++; });
slot1Called++;
QObject::disconnect(*c1);
});
de.clear();
connect(&obj, &SenderObject::signal1, [&slot2Called] { slot2Called++; });
obj.emitSignal1();
QCOMPARE(slot1Called, 1);
QCOMPARE(slot2Called, 2); // because also called from ~DestroyEmit
QCOMPARE(slot3Called, 1);
}
}
void tst_QObject::disconnectDisconnects()
{
// Test what happens if the destructor of an functor slot also disconnects more slot;
SenderObject s1;
QScopedPointer<QObject> receiver(new QObject);
auto s2 = QSharedPointer<SenderObject>::create();
QPointer<QObject> s2_tracker = s2.data();
int count = 0;
connect(&s1, &SenderObject::signal1, [&count] { count++; }); // α
connect(&s1, &SenderObject::signal1, receiver.data(), [s2] { QFAIL("!!"); }); // β
connect(s2.data(), &SenderObject::signal1, receiver.data(), [] { QFAIL("!!"); });
connect(&s1, &SenderObject::signal2, receiver.data(), [] { QFAIL("!!"); });
connect(s2.data(), &SenderObject::signal2, receiver.data(), [] { QFAIL("!!"); });
connect(&s1, &SenderObject::signal1, [&count] { count++; }); // γ
connect(&s1, &SenderObject::signal2, [&count] { count++; }); // δ
s2.clear();
QVERIFY(s2_tracker);
receiver
.reset(); // this will delete the receiver which must also delete s2 as β is disconnected
QVERIFY(!s2_tracker);
// test that the data structures are still in order
s1.emitSignal1();
QCOMPARE(count, 2); // α + γ
s1.emitSignal2();
QCOMPARE(count, 3); // + δ
}
// Test for QtPrivate::HasQ_OBJECT_Macro
Q_STATIC_ASSERT(QtPrivate::HasQ_OBJECT_Macro<tst_QObject>::Value);
Q_STATIC_ASSERT(!QtPrivate::HasQ_OBJECT_Macro<SiblingDeleter>::Value);
QTEST_MAIN(tst_QObject)
#include "tst_qobject.moc"