From: Ivailo Monev Date: Sun, 29 Aug 2021 20:48:27 +0000 (+0300) Subject: add QObject test X-Git-Tag: 4.12.0~1891 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=5ffe929bcde2845d5b92f4ee412a0ee2752341b4;p=kde%2FKatie.git add QObject test Signed-off-by: Ivailo Monev --- diff --git a/tests/auto/qobject/CMakeLists.txt b/tests/auto/qobject/CMakeLists.txt new file mode 100644 index 000000000..d36faae56 --- /dev/null +++ b/tests/auto/qobject/CMakeLists.txt @@ -0,0 +1,14 @@ +katie_test(tst_qobject + ${CMAKE_CURRENT_SOURCE_DIR}/tst_qobject.cpp +) + +target_link_libraries(tst_qobject KtNetwork) + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) +katie_setup_target(signalbug + ${CMAKE_CURRENT_SOURCE_DIR}/signalbug.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/signalbug.h +) +add_executable(signalbug ${signalbug_SOURCES}) + +target_link_libraries(signalbug KtCore) diff --git a/tests/auto/qobject/signalbug.cpp b/tests/auto/qobject/signalbug.cpp new file mode 100644 index 000000000..f7b1b4aa5 --- /dev/null +++ b/tests/auto/qobject/signalbug.cpp @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2016 Ivailo Monev +** +** This file is part of the test suite of the Katie Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "signalbug.h" + +#include +#include + +#include + +static int Step = 0; +Sender RandomSender (0, 0); + + +void TRACE (int step, const char *name) +{ + for (int t = 0; t < step - 1; t++) + fprintf (stderr, "\t"); + fprintf (stderr, "Step %d: %s\n", step, name); + return; +} + + +Receiver::Receiver () + : QObject () +{ +} + +void Receiver::received () +{ + ::Step++; + const int stepCopy = ::Step; + TRACE (stepCopy, "Receiver::received()"); + if (::Step != 2 && ::Step != 4) + qFatal("%s: Incorrect Step: %d (should be 2 or 4)", Q_FUNC_INFO, ::Step); + + if (::Step == 2) + s->fire (); + + fprintf (stderr, "Receiver<%s>::received() sender=%s\n", + (const char *) objectName ().toAscii (), sender ()->metaObject()->className()); + + TRACE (stepCopy, "ends Receiver::received()"); +} + + +Disconnector::Disconnector () + : QObject () +{ +} + +void Disconnector::received () +{ + ::Step++; + const int stepCopy = ::Step; + TRACE (stepCopy, "Disconnector::received()"); + if (::Step != 5 && ::Step != 6) + qFatal("%s: Incorrect Step: %d (should be 5 or 6)", Q_FUNC_INFO, ::Step); + + fprintf (stderr, "Disconnector<%s>::received() sender=%s\n", + (const char *) objectName ().toAscii (), sender ()->metaObject()->className()); + if (sender () == 0) + fprintf (stderr, "WE SHOULD NOT BE RECEIVING THIS SIGNAL\n"); + + if (::Step == 5) + { + disconnect (s, SIGNAL (fired ()), s->d, SLOT (received ())); + + connect (&RandomSender, SIGNAL (fired ()), s->d, SLOT (received ())); + } + + TRACE (stepCopy, "ends Disconnector::received()"); +} + + +Sender::Sender (Receiver *r, Disconnector *d) + : QObject () +{ + this->r = r; this->d = d; + if (r) + connect (this, SIGNAL (fired ()), r, SLOT (received ())); + if (d) + connect (this, SIGNAL (fired ()), d, SLOT (received ())); +}; + +void Sender::fire () +{ + ::Step++; + const int stepCopy = ::Step; + TRACE (stepCopy, "Sender::fire()"); + if (::Step != 1 && ::Step != 3) + qFatal("%s: Incorrect Step: %d (should be 1 or 3)", Q_FUNC_INFO, ::Step); + + emit fired (); + TRACE (stepCopy, "ends Sender::fire()"); +} + + +int main (int argc, char *argv []) +{ + QCoreApplication app (argc, argv); + + Receiver r; + Disconnector d; + Sender s (&r, &d); + + r.s = &s; + d.s = &s; + + + ::Step = 0; + s.fire (); + return 0; +} + +#include "moc_signalbug.h" diff --git a/tests/auto/qobject/signalbug.h b/tests/auto/qobject/signalbug.h new file mode 100644 index 000000000..5aeceec83 --- /dev/null +++ b/tests/auto/qobject/signalbug.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2016 Ivailo Monev +** +** This file is part of the test suite of the Katie Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SIGNAL_BUG_H +#define SIGNAL_BUG_H + + +#include + + +class Sender; + + +class Receiver : public QObject +{ + Q_OBJECT + +public: + Receiver (); + virtual ~Receiver () {} + +protected slots: + void received (); + +public: + Sender *s; +}; + + +class Disconnector : public QObject +{ + Q_OBJECT + +public: + Disconnector (); + virtual ~Disconnector () {} + +protected slots: + void received (); + +public: + Sender *s; +}; + + +class Sender : public QObject +{ +Q_OBJECT + +public: + Sender (Receiver *r, Disconnector *d); + virtual ~Sender () {} + + void fire (); + +signals: + void fired (); + +public: + Receiver *r; + Disconnector *d; +}; + + +#endif // SIGNAL_BUG_H diff --git a/tests/auto/qobject/tst_qobject.cpp b/tests/auto/qobject/tst_qobject.cpp new file mode 100644 index 000000000..f6497445a --- /dev/null +++ b/tests/auto/qobject/tst_qobject.cpp @@ -0,0 +1,3723 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2016 Ivailo Monev +** +** This file is part of the test suite of the Katie Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "qobject.h" +#include "qobject_p.h" + +#include + +//TESTED_CLASS= +//TESTED_FILES= + +class tst_QObject : public QObject +{ + Q_OBJECT + +public: + tst_QObject(); + virtual ~tst_QObject(); + + +public slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); +private slots: + void disconnect(); + void connectByName(); + void connectSignalsToSignalsWithDefaultArguments(); + void receivers(); + void normalize(); + void qobject_castTemplate(); + void findChildren(); + void connectDisconnectNotify_data(); + void connectDisconnectNotify(); + void emitInDefinedOrder(); + void customTypes(); + void streamCustomTypes(); + void metamethod(); + void namespaces(); + void threadSignalEmissionCrash(); + void thread(); + void moveToThread(); + void sender(); + void declareInterface(); + void qpointerResetBeforeDestroyedSignal(); + void childDeletesItsSibling(); + void dynamicProperties(); + void floatProperty(); + void qrealProperty(); + void property(); + void recursiveSignalEmission(); + void blockingQueuedConnection(); + void compatibilityChildInsertedEvents(); + void installEventFilter(); + void deleteSelfInSlot(); + void disconnectSelfInSlotAndDeleteAfterEmit(); + void dumpObjectInfo(); + void connectToSender(); + void qobjectConstCast(); + void uniqConnection(); + void interfaceIid(); + void deleteQObjectWhenDeletingEvent(); + void overloads(); + void isSignalConnected(); + void qMetaObjectConnect(); + void qMetaObjectDisconnectOne(); + void sameName(); + void connectByMetaMethods(); + void connectByMetaMethodSlotInsteadOfSignal(); + void connectConstructorByMetaMethod(); + void disconnectByMetaMethod(); + void disconnectNotSignalMetaMethod(); + void autoConnectionBehavior(); + void baseDestroyed(); +protected: +}; + +tst_QObject::tst_QObject() +{ + +} + +tst_QObject::~tst_QObject() +{ + +} + +void tst_QObject::initTestCase() +{ +} + +void tst_QObject::cleanupTestCase() +{ +} + +void tst_QObject::init() +{ +} + +void tst_QObject::cleanup() +{ +} + +class SenderObject : public QObject +{ + Q_OBJECT + +public: + SenderObject() : aPublicSlotCalled(0), recursionCount(0) {} + + void emitSignal1AfterRecursion() + { + if (recursionCount++ < 100) + emitSignal1AfterRecursion(); + else + emitSignal1(); + } + + void emitSignal1() { emit signal1(); } + void emitSignal2() { emit signal2(); } + void emitSignal3() { emit signal3(); } + void emitSignal4() { emit signal4(); } + +signals: + void signal1(); + void signal2(); + void signal3(); + void signal4(); + void signal5(); + +public slots: + void aPublicSlot() { aPublicSlotCalled++; } + +public: + Q_INVOKABLE void invoke1(){} + Q_SCRIPTABLE void sinvoke1(){} + int aPublicSlotCalled; +protected: + Q_INVOKABLE void invoke2(){} + Q_INVOKABLE void invoke2(int){} + Q_SCRIPTABLE void sinvoke2(){} +private: + Q_INVOKABLE void invoke3(int hinz = 0, int kunz = 0){Q_UNUSED(hinz) Q_UNUSED(kunz)} + Q_SCRIPTABLE void sinvoke3(){} + + int recursionCount; +}; + +class ReceiverObject : public QObject +{ + Q_OBJECT + +public: + ReceiverObject() : sequence_slot1( 0 ), + sequence_slot2( 0 ), + sequence_slot3( 0 ), + sequence_slot4( 0 ) {} + + void reset() { + sequence_slot4 = 0; + sequence_slot3 = 0; + sequence_slot2 = 0; + sequence_slot1 = 0; + count_slot1 = 0; + count_slot2 = 0; + count_slot3 = 0; + count_slot4 = 0; + } + + int sequence_slot1; + int sequence_slot2; + int sequence_slot3; + int sequence_slot4; + int count_slot1; + int count_slot2; + int count_slot3; + int count_slot4; + + bool called(int slot) { + switch (slot) { + case 1: return sequence_slot1; + case 2: return sequence_slot2; + case 3: return sequence_slot3; + case 4: return sequence_slot4; + default: return false; + } + } + + static int sequence; + +public slots: + void slot1() { sequence_slot1 = ++sequence; count_slot1++; } + void slot2() { sequence_slot2 = ++sequence; count_slot2++; } + void slot3() { sequence_slot3 = ++sequence; count_slot3++; } + void slot4() { sequence_slot4 = ++sequence; count_slot4++; } + +}; + +int ReceiverObject::sequence = 0; + +void tst_QObject::disconnect() +{ + SenderObject *s = new SenderObject; + ReceiverObject *r1 = new ReceiverObject; + ReceiverObject *r2 = new ReceiverObject; + + connect( s, SIGNAL( signal1() ), r1, SLOT( slot1() ) ); + + connect( s, SIGNAL( signal2() ), r1, SLOT( slot2() ) ); + connect( s, SIGNAL( signal3() ), r1, SLOT( slot3() ) ); + connect( s, SIGNAL( signal4() ), r1, SLOT( slot4() ) ); + + s->emitSignal1(); + s->emitSignal2(); + s->emitSignal3(); + s->emitSignal4(); + + QCOMPARE( r1->called(1), true ); + QCOMPARE( r1->called(2), true ); + QCOMPARE( r1->called(3), true ); + QCOMPARE( r1->called(4), true ); + r1->reset(); + + // usual disconnect with all parameters given + bool ret = QObject::disconnect( s, SIGNAL( signal1() ), r1, SLOT( slot1() ) ); + + s->emitSignal1(); + + QCOMPARE( r1->called(1), false ); + r1->reset(); + + QCOMPARE( ret, true ); + ret = QObject::disconnect( s, SIGNAL( signal1() ), r1, SLOT( slot1() ) ); + QCOMPARE( ret, false ); + + // disconnect all signals from s from all slots from r1 + QObject::disconnect( s, 0, r1, 0 ); + + s->emitSignal2(); + s->emitSignal3(); + s->emitSignal4(); + + QCOMPARE( r1->called(2), false ); + QCOMPARE( r1->called(3), false ); + QCOMPARE( r1->called(4), false ); + r1->reset(); + + connect( s, SIGNAL( signal1() ), r1, SLOT( slot1() ) ); + connect( s, SIGNAL( signal1() ), r1, SLOT( slot2() ) ); + connect( s, SIGNAL( signal1() ), r1, SLOT( slot3() ) ); + connect( s, SIGNAL( signal2() ), r1, SLOT( slot4() ) ); + + // disconnect s's signal1() from all slots of r1 + QObject::disconnect( s, SIGNAL( signal1() ), r1, 0 ); + + s->emitSignal1(); + s->emitSignal2(); + + QCOMPARE( r1->called(1), false ); + QCOMPARE( r1->called(2), false ); + QCOMPARE( r1->called(3), false ); + QCOMPARE( r1->called(4), true ); + r1->reset(); + // make sure all is disconnected again + QObject::disconnect( s, 0, r1, 0 ); + + connect( s, SIGNAL( signal1() ), r1, SLOT( slot1() ) ); + connect( s, SIGNAL( signal1() ), r2, SLOT( slot1() ) ); + connect( s, SIGNAL( signal2() ), r1, SLOT( slot2() ) ); + connect( s, SIGNAL( signal2() ), r2, SLOT( slot2() ) ); + connect( s, SIGNAL( signal3() ), r1, SLOT( slot3() ) ); + connect( s, SIGNAL( signal3() ), r2, SLOT( slot3() ) ); + + // disconnect signal1() from all receivers + QObject::disconnect( s, SIGNAL( signal1() ), 0, 0 ); + s->emitSignal1(); + s->emitSignal2(); + s->emitSignal3(); + + QCOMPARE( r1->called(1), false ); + QCOMPARE( r2->called(1), false ); + QCOMPARE( r1->called(2), true ); + QCOMPARE( r2->called(2), true ); + QCOMPARE( r1->called(2), true ); + QCOMPARE( r2->called(2), true ); + + r1->reset(); + r2->reset(); + + // disconnect all signals of s from all receivers + QObject::disconnect( s, 0, 0, 0 ); + + QCOMPARE( r1->called(2), false ); + QCOMPARE( r2->called(2), false ); + QCOMPARE( r1->called(2), false ); + QCOMPARE( r2->called(2), false ); + + delete r2; + delete r1; + delete s; +} + +class AutoConnectSender : public QObject +{ + Q_OBJECT + +public: + AutoConnectSender(QObject *parent) + : QObject(parent) + {} + + void emitSignalNoParams() { emit signalNoParams(); } + void emitSignalWithParams(int i) { emit signalWithParams(i); } + void emitSignalWithParams(int i, QString string) { emit signalWithParams(i, string); } + void emitSignalManyParams(int i1, int i2, int i3, QString string, bool onoff) { emit signalManyParams(i1, i2, i3, string, onoff); } + void emitSignalManyParams2(int i1, int i2, int i3, QString string, bool onoff) { emit signalManyParams2(i1, i2, i3, string, onoff); } + void emitSignalLoopBack() { emit signalLoopBack(); } + +signals: + void signalNoParams(); + void signalWithParams(int i); + void signalWithParams(int i, QString string); + void signalManyParams(int i1, int i2, int i3, QString string, bool onoff); + void signalManyParams(int i1, int i2, int i3, QString string, bool onoff, bool); + void signalManyParams2(int i1, int i2, int i3, QString string, bool onoff); + void signalLoopBack(); +}; + +class AutoConnectReceiver : public QObject +{ + Q_OBJECT + +public: + AutoConnectReceiver() + { + reset(); + + connect(this, SIGNAL(on_Sender_signalLoopBack()), this, SLOT(slotLoopBack())); + } + + void reset() { + called_slot10 = 0; + called_slot9 = 0; + called_slot8 = 0; + called_slot7 = 0; + called_slot6 = 0; + called_slot5 = 0; + called_slot4 = 0; + called_slot3 = 0; + called_slot2 = 0; + called_slot1 = 0; + } + + int called_slot1; + int called_slot2; + int called_slot3; + int called_slot4; + int called_slot5; + int called_slot6; + int called_slot7; + int called_slot8; + int called_slot9; + int called_slot10; + + bool called(int slot) { + switch (slot) { + case 1: return called_slot1; + case 2: return called_slot2; + case 3: return called_slot3; + case 4: return called_slot4; + case 5: return called_slot5; + case 6: return called_slot6; + case 7: return called_slot7; + case 8: return called_slot8; + case 9: return called_slot9; + case 10: return called_slot10; + default: return false; + } + } + +public slots: + void on_Sender_signalNoParams() { ++called_slot1; } + void on_Sender_signalWithParams(int i = 0) { ++called_slot2; Q_UNUSED(i); } + void on_Sender_signalWithParams(int i, QString string) { ++called_slot3; Q_UNUSED(i);Q_UNUSED(string); } + void on_Sender_signalManyParams() { ++called_slot4; } + void on_Sender_signalManyParams(int i1, int i2, int i3, QString string, bool onoff) { ++called_slot5; Q_UNUSED(i1);Q_UNUSED(i2);Q_UNUSED(i3);Q_UNUSED(string);Q_UNUSED(onoff); } + void on_Sender_signalManyParams(int i1, int i2, int i3, QString string, bool onoff, bool dummy) + { ++called_slot6; Q_UNUSED(i1);Q_UNUSED(i2);Q_UNUSED(i3);Q_UNUSED(string);Q_UNUSED(onoff); Q_UNUSED(dummy);} + void on_Sender_signalManyParams2(int i1, int i2, int i3, QString string, bool onoff) + { ++called_slot7; Q_UNUSED(i1);Q_UNUSED(i2);Q_UNUSED(i3);Q_UNUSED(string);Q_UNUSED(onoff); } + void slotLoopBack() { ++called_slot8; } + +protected slots: + void o() { ++called_slot9; } + void on() { ++called_slot10; } + +signals: + void on_Sender_signalLoopBack(); +}; + +void tst_QObject::connectByName() +{ + AutoConnectReceiver receiver; + AutoConnectSender sender(&receiver); + sender.setObjectName("Sender"); + + QMetaObject::connectSlotsByName(&receiver); + + sender.emitSignalNoParams(); + QCOMPARE(receiver.called(1), true); + QCOMPARE(receiver.called(2), false); + QCOMPARE(receiver.called(3), false); + QCOMPARE(receiver.called(4), false); + QCOMPARE(receiver.called(5), false); + QCOMPARE(receiver.called(6), false); + QCOMPARE(receiver.called(7), false); + QCOMPARE(receiver.called(8), false); + QCOMPARE(receiver.called(9), false); + QCOMPARE(receiver.called(10), false); + receiver.reset(); + + sender.emitSignalWithParams(0); + QCOMPARE(receiver.called(1), false); + QCOMPARE(receiver.called(2), true); + QCOMPARE(receiver.called(3), false); + QCOMPARE(receiver.called(4), false); + QCOMPARE(receiver.called(5), false); + QCOMPARE(receiver.called(6), false); + QCOMPARE(receiver.called(7), false); + QCOMPARE(receiver.called(8), false); + QCOMPARE(receiver.called(9), false); + QCOMPARE(receiver.called(10), false); + receiver.reset(); + + sender.emitSignalWithParams(0, "string"); + QCOMPARE(receiver.called(1), false); + QCOMPARE(receiver.called(2), false); + QCOMPARE(receiver.called(3), true); + QCOMPARE(receiver.called(4), false); + QCOMPARE(receiver.called(5), false); + QCOMPARE(receiver.called(6), false); + QCOMPARE(receiver.called(7), false); + QCOMPARE(receiver.called(8), false); + QCOMPARE(receiver.called(9), false); + QCOMPARE(receiver.called(10), false); + receiver.reset(); + + sender.emitSignalManyParams(1, 2, 3, "string", true); + QCOMPARE(receiver.called(1), false); + QCOMPARE(receiver.called(2), false); + QCOMPARE(receiver.called(3), false); + QCOMPARE(receiver.called(4), true); + QCOMPARE(receiver.called(5), true); + QCOMPARE(receiver.called(6), false); + QCOMPARE(receiver.called(7), false); + QCOMPARE(receiver.called(8), false); + QCOMPARE(receiver.called(9), false); + QCOMPARE(receiver.called(10), false); + receiver.reset(); + + sender.emitSignalManyParams2(1, 2, 3, "string", true); + QCOMPARE(receiver.called(1), false); + QCOMPARE(receiver.called(2), false); + QCOMPARE(receiver.called(3), false); + QCOMPARE(receiver.called(4), false); + QCOMPARE(receiver.called(5), false); + QCOMPARE(receiver.called(6), false); + QCOMPARE(receiver.called(7), true); + QCOMPARE(receiver.called(8), false); + QCOMPARE(receiver.called(9), false); + QCOMPARE(receiver.called(10), false); + receiver.reset(); + + sender.emitSignalLoopBack(); + QCOMPARE(receiver.called(1), false); + QCOMPARE(receiver.called(2), false); + QCOMPARE(receiver.called(3), false); + QCOMPARE(receiver.called(4), false); + QCOMPARE(receiver.called(5), false); + QCOMPARE(receiver.called(6), false); + QCOMPARE(receiver.called(7), false); + QCOMPARE(receiver.called(8), true); + QCOMPARE(receiver.called(9), false); + QCOMPARE(receiver.called(10), false); + receiver.reset(); +} + +void tst_QObject::qobject_castTemplate() +{ + QObject *o = 0; + QVERIFY( !::qobject_cast(o) ); + + o = new SenderObject; + QVERIFY( ::qobject_cast(o) ); + QVERIFY( ::qobject_cast(o) ); + QVERIFY( !::qobject_cast(o) ); + delete o; +} + +void tst_QObject::findChildren() +{ + QObject o; + QObject o1(&o); + QObject o2(&o); + QObject o11(&o1); + QObject o12(&o1); + QObject o111(&o11); + QObject unnamed(&o); + QTimer t1(&o); + QTimer t121(&o12); + QTimer emptyname(&o); + + o.setObjectName("o"); + o1.setObjectName("o1"); + o2.setObjectName("o2"); + o11.setObjectName("o11"); + o12.setObjectName("o12"); + o111.setObjectName("o111"); + t1.setObjectName("t1"); + t121.setObjectName("t121"); + emptyname.setObjectName(""); + + QObject *op = 0; + + op = o.findChild("o1"); + QCOMPARE(op, &o1); + op = o.findChild("o2"); + QCOMPARE(op, &o2); + op = o.findChild("o11"); + QCOMPARE(op, &o11); + op = o.findChild("o12"); + QCOMPARE(op, &o12); + op = o.findChild("o111"); + QCOMPARE(op, &o111); + op = o.findChild("t1"); + QCOMPARE(op, static_cast(&t1)); + op = o.findChild("t121"); + QCOMPARE(op, static_cast(&t121)); + op = o.findChild("t1"); + QCOMPARE(op, static_cast(&t1)); + op = o.findChild("t121"); + QCOMPARE(op, static_cast(&t121)); + op = o.findChild("o12"); + QCOMPARE(op, static_cast(0)); + op = o.findChild("o"); + QCOMPARE(op, static_cast(0)); + op = o.findChild("harry"); + QCOMPARE(op, static_cast(0)); + op = o.findChild("o1"); + QCOMPARE(op, &o1); + + QList l; + QList tl; + + l = o.findChildren("o1"); + QCOMPARE(l.size(), 1); + QCOMPARE(l.at(0), &o1); + l = o.findChildren("o2"); + QCOMPARE(l.size(), 1); + QCOMPARE(l.at(0), &o2); + l = o.findChildren("o11"); + QCOMPARE(l.size(), 1); + QCOMPARE(l.at(0), &o11); + l = o.findChildren("o12"); + QCOMPARE(l.size(), 1); + QCOMPARE(l.at(0), &o12); + l = o.findChildren("o111"); + QCOMPARE(l.size(), 1); + QCOMPARE(l.at(0), &o111); + l = o.findChildren("t1"); + QCOMPARE(l.size(), 1); + QCOMPARE(l.at(0), static_cast(&t1)); + l = o.findChildren("t121"); + QCOMPARE(l.size(), 1); + QCOMPARE(l.at(0), static_cast(&t121)); + tl = o.findChildren("t1"); + QCOMPARE(tl.size(), 1); + QCOMPARE(tl.at(0), &t1); + tl = o.findChildren("t121"); + QCOMPARE(tl.size(), 1); + QCOMPARE(tl.at(0), &t121); + l = o.findChildren("o"); + QCOMPARE(l.size(), 0); + l = o.findChildren("harry"); + QCOMPARE(l.size(), 0); + tl = o.findChildren("o12"); + QCOMPARE(tl.size(), 0); + l = o.findChildren("o1"); + QCOMPARE(l.size(), 1); + QCOMPARE(l.at(0), &o1); + + l = o.findChildren(QRegExp("o.*")); + QCOMPARE(l.size(), 5); + QVERIFY(l.contains(&o1)); + QVERIFY(l.contains(&o2)); + QVERIFY(l.contains(&o11)); + QVERIFY(l.contains(&o12)); + QVERIFY(l.contains(&o111)); + l = o.findChildren(QRegExp("t.*")); + QCOMPARE(l.size(), 2); + QVERIFY(l.contains(&t1)); + QVERIFY(l.contains(&t121)); + tl = o.findChildren(QRegExp(".*")); + QCOMPARE(tl.size(), 3); + QVERIFY(tl.contains(&t1)); + QVERIFY(tl.contains(&t121)); + tl = o.findChildren(QRegExp("o.*")); + QCOMPARE(tl.size(), 0); + l = o.findChildren(QRegExp("harry")); + QCOMPARE(l.size(), 0); + + // empty and null string check + op = o.findChild(); + QCOMPARE(op, &o1); + op = o.findChild(""); + QCOMPARE(op, &unnamed); + op = o.findChild("unnamed"); + QCOMPARE(op, static_cast(0)); + + l = o.findChildren(); + QCOMPARE(l.size(), 9); + l = o.findChildren(""); + QCOMPARE(l.size(), 2); + l = o.findChildren("unnamed"); + QCOMPARE(l.size(), 0); + + tl = o.findChildren("t1"); + QCOMPARE(tl.size(), 1); + QCOMPARE(tl.at(0), &t1); +} + + +class NotifyObject : public SenderObject, public ReceiverObject +{ +public: + NotifyObject() : SenderObject(), ReceiverObject() + {} + + QString org_signal; + QString nw_signal; + +protected: + void connectNotify( const char *signal ) + { + org_signal = signal; + nw_signal = QMetaObject::normalizedSignature(signal); + }; + void disconnectNotify( const char *signal ) + { + org_signal = signal; + nw_signal = QMetaObject::normalizedSignature(signal); + }; +}; + +void tst_QObject::connectDisconnectNotify_data() +{ + QTest::addColumn("a_signal"); + QTest::addColumn("a_slot"); + + QTest::newRow("combo1") << SIGNAL( signal1() ) << SLOT( slot1() ); + QTest::newRow("combo2") << SIGNAL( signal2(void) ) << SLOT( slot2( ) ); + QTest::newRow("combo3") << SIGNAL( signal3( ) ) << SLOT( slot3(void) ); + QTest::newRow("combo4") << SIGNAL( signal4( void ) )<< SLOT( slot4( void ) ); +} + +void tst_QObject::connectDisconnectNotify() +{ + NotifyObject *s = new NotifyObject; + NotifyObject *r = new NotifyObject; + + QFETCH(QString, a_signal); + QFETCH(QString, a_slot); + + // Test connectNotify + connect( (SenderObject*)s, a_signal.toLatin1(), (ReceiverObject*)r, a_slot.toLatin1() ); + QCOMPARE( s->org_signal, s->nw_signal ); + QCOMPARE( s->org_signal.toLatin1(), QMetaObject::normalizedSignature(a_signal.toLatin1().constData()) ); + + // Test disconnectNotify + QObject::disconnect( (SenderObject*)s, a_signal.toLatin1(), (ReceiverObject*)r, a_slot.toLatin1() ); + QCOMPARE( s->org_signal, s->nw_signal ); + QCOMPARE( s->org_signal.toLatin1(), QMetaObject::normalizedSignature(a_signal.toLatin1().constData()) ); + + // Reconnect + connect( (SenderObject*)s, a_signal.toLatin1(), (ReceiverObject*)r, a_slot.toLatin1() ); + // Test disconnectNotify for a complete disconnect + ((SenderObject*)s)->disconnect((ReceiverObject*)r); + + // Obtaining meta methods + int signalIndx = ((SenderObject*)s)->metaObject()->indexOfSignal( + QMetaObject::normalizedSignature(a_signal.toLatin1().constData()+1).constData()); + int methodIndx = ((ReceiverObject*)r)->metaObject()->indexOfMethod( + QMetaObject::normalizedSignature(a_slot.toLatin1().constData()+1).constData()); + QMetaMethod signal = ((SenderObject*)s)->metaObject()->method(signalIndx); + QMetaMethod method = ((ReceiverObject*)r)->metaObject()->method(methodIndx); + + // Test connectNotify when connecting by QMetaMethod + connect( (SenderObject*)s, signal, (ReceiverObject*)r, method ); + QCOMPARE( s->org_signal, s->nw_signal ); + QCOMPARE( s->org_signal.toLatin1(), QMetaObject::normalizedSignature(a_signal.toLatin1().constData()) ); + + // Test disconnectNotify when disconnecting by QMetaMethod + QObject::disconnect( (SenderObject*)s, signal, (ReceiverObject*)r, method ); + QCOMPARE( s->org_signal, s->nw_signal ); + QCOMPARE( s->org_signal.toLatin1(), QMetaObject::normalizedSignature(a_signal.toLatin1().constData()) ); + + delete s; + delete r; +} + +class SequenceObject : public ReceiverObject +{ + Q_OBJECT + +public: + QObject *next; + SequenceObject() : next(0) { } + +public slots: + void slot1_disconnectThis() + { + slot1(); + disconnect(sender(), SIGNAL(signal1()), this, SLOT(slot1_disconnectThis())); + } + + void slot2_reconnectThis() + { + slot2(); + + const QObject *s = sender(); + disconnect(s, SIGNAL(signal1()), this, SLOT(slot2_reconnectThis())); + connect(s, SIGNAL(signal1()), this, SLOT(slot2_reconnectThis())); + } + + void slot1_disconnectNext() + { + slot1(); + disconnect(sender(), SIGNAL(signal1()), next, SLOT(slot1())); + } + + void slot2_reconnectNext() + { + slot2(); + + // modify the connection list in 'this' + disconnect(sender(), SIGNAL(signal1()), next, SLOT(slot2())); + connect(sender(), SIGNAL(signal1()), next, SLOT(slot2())); + + // modify the sender list in 'this' + connect(next, SIGNAL(destroyed()), this, SLOT(deleteLater())); + connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(deleteLater())); + disconnect(next, SIGNAL(destroyed()), this, SLOT(deleteLater())); + disconnect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(deleteLater())); + } + + void slot1_deleteNext() + { + slot1(); + delete next; + } + + void slot2_deleteSender() + { + slot2(); + delete sender(); + } +}; + +void tst_QObject::emitInDefinedOrder() +{ + SenderObject sender; + ReceiverObject receiver1, receiver2, receiver3, receiver4; + + connect(&sender, SIGNAL(signal1()), &receiver1, SLOT(slot1())); + connect(&sender, SIGNAL(signal1()), &receiver2, SLOT(slot1())); + connect(&sender, SIGNAL(signal1()), &receiver3, SLOT(slot1())); + connect(&sender, SIGNAL(signal1()), &receiver4, SLOT(slot1())); + connect(&sender, SIGNAL(signal1()), &receiver1, SLOT(slot2())); + connect(&sender, SIGNAL(signal1()), &receiver2, SLOT(slot2())); + connect(&sender, SIGNAL(signal1()), &receiver3, SLOT(slot2())); + connect(&sender, SIGNAL(signal1()), &receiver4, SLOT(slot2())); + + int sequence; + ReceiverObject::sequence = sequence = 0; + sender.emitSignal1(); + QCOMPARE(receiver1.sequence_slot1, ++sequence); + QCOMPARE(receiver2.sequence_slot1, ++sequence); + QCOMPARE(receiver3.sequence_slot1, ++sequence); + QCOMPARE(receiver4.sequence_slot1, ++sequence); + QCOMPARE(receiver1.sequence_slot2, ++sequence); + QCOMPARE(receiver2.sequence_slot2, ++sequence); + QCOMPARE(receiver3.sequence_slot2, ++sequence); + QCOMPARE(receiver4.sequence_slot2, ++sequence); + + QObject::disconnect(&sender, SIGNAL(signal1()), &receiver2, SLOT(slot1())); + connect(&sender, SIGNAL(signal1()), &receiver2, SLOT(slot1())); + + ReceiverObject::sequence = sequence = 0; + sender.emitSignal1(); + QCOMPARE(receiver1.sequence_slot1, ++sequence); + QCOMPARE(receiver3.sequence_slot1, ++sequence); + QCOMPARE(receiver4.sequence_slot1, ++sequence); + QCOMPARE(receiver1.sequence_slot2, ++sequence); + QCOMPARE(receiver2.sequence_slot2, ++sequence); + QCOMPARE(receiver3.sequence_slot2, ++sequence); + QCOMPARE(receiver4.sequence_slot2, ++sequence); + QCOMPARE(receiver2.sequence_slot1, ++sequence); + + QObject::disconnect(&sender, SIGNAL(signal1()), &receiver1, SLOT(slot1())); + connect(&sender, SIGNAL(signal1()), &receiver1, SLOT(slot1())); + + ReceiverObject::sequence = sequence = 0; + sender.emitSignal1(); + QCOMPARE(receiver3.sequence_slot1, ++sequence); + QCOMPARE(receiver4.sequence_slot1, ++sequence); + QCOMPARE(receiver1.sequence_slot2, ++sequence); + QCOMPARE(receiver2.sequence_slot2, ++sequence); + QCOMPARE(receiver3.sequence_slot2, ++sequence); + QCOMPARE(receiver4.sequence_slot2, ++sequence); + QCOMPARE(receiver2.sequence_slot1, ++sequence); + QCOMPARE(receiver1.sequence_slot1, ++sequence); + + // ensure emission order even if the connections change during emission + SenderObject *sender2 = new SenderObject; + SequenceObject seq1, seq2, *seq3 = new SequenceObject, seq4; + seq1.next = &seq2; + seq2.next = seq3; + seq3->next = &seq4; + + // try 1 + connect(sender2, SIGNAL(signal1()), &seq1, SLOT(slot1_disconnectThis())); + connect(sender2, SIGNAL(signal1()), &seq2, SLOT(slot1_disconnectNext())); + connect(sender2, SIGNAL(signal1()), seq3, SLOT(slot1())); + connect(sender2, SIGNAL(signal1()), &seq4, SLOT(slot1())); + connect(sender2, SIGNAL(signal1()), &seq1, SLOT(slot2_reconnectThis())); + connect(sender2, SIGNAL(signal1()), &seq2, SLOT(slot2_reconnectNext())); + connect(sender2, SIGNAL(signal1()), seq3, SLOT(slot2())); + connect(sender2, SIGNAL(signal1()), &seq4, SLOT(slot2())); + + SequenceObject::sequence = sequence = 0; + sender2->emitSignal1(); + QCOMPARE(seq1.called(1), true); + QCOMPARE(seq2.called(1), true); + QCOMPARE(seq3->called(1), false); + QCOMPARE(seq4.called(1), true); + QCOMPARE(seq1.called(2), true); + QCOMPARE(seq2.called(2), true); + QCOMPARE(seq3->called(2), false); + QCOMPARE(seq4.called(2), true); + QCOMPARE(seq1.sequence_slot1, ++sequence); + QCOMPARE(seq2.sequence_slot1, ++sequence); + QCOMPARE(seq4.sequence_slot1, ++sequence); + QCOMPARE(seq1.sequence_slot2, ++sequence); + QCOMPARE(seq2.sequence_slot2, ++sequence); + QCOMPARE(seq4.sequence_slot2, ++sequence); + + QObject::disconnect(sender2, 0, &seq1, 0); + QObject::disconnect(sender2, 0, &seq2, 0); + QObject::disconnect(sender2, 0, seq3, 0); + QObject::disconnect(sender2, 0, &seq4, 0); + seq1.reset(); + seq2.reset(); + seq3->reset(); + seq4.reset(); + + // try 2 + connect(sender2, SIGNAL(signal1()), &seq1, SLOT(slot2_reconnectThis())); + connect(sender2, SIGNAL(signal1()), &seq2, SLOT(slot2_reconnectNext())); + connect(sender2, SIGNAL(signal1()), seq3, SLOT(slot2())); + connect(sender2, SIGNAL(signal1()), &seq4, SLOT(slot2())); + connect(sender2, SIGNAL(signal1()), &seq1, SLOT(slot1_disconnectThis())); + connect(sender2, SIGNAL(signal1()), &seq2, SLOT(slot1_disconnectNext())); + connect(sender2, SIGNAL(signal1()), seq3, SLOT(slot1())); + connect(sender2, SIGNAL(signal1()), &seq4, SLOT(slot1())); + + SequenceObject::sequence = sequence = 0; + sender2->emitSignal1(); + QCOMPARE(seq1.called(2), true); + QCOMPARE(seq2.called(2), true); + QCOMPARE(seq3->called(2), false); + QCOMPARE(seq4.called(2), true); + QCOMPARE(seq1.called(1), true); + QCOMPARE(seq2.called(1), true); + QCOMPARE(seq3->called(1), false); + QCOMPARE(seq4.called(1), true); + QCOMPARE(seq1.sequence_slot2, ++sequence); + QCOMPARE(seq2.sequence_slot2, ++sequence); + QCOMPARE(seq4.sequence_slot2, ++sequence); + QCOMPARE(seq1.sequence_slot1, ++sequence); + QCOMPARE(seq2.sequence_slot1, ++sequence); + QCOMPARE(seq4.sequence_slot1, ++sequence); + + QObject::disconnect(sender2, 0, &seq1, 0); + QObject::disconnect(sender2, 0, &seq2, 0); + QObject::disconnect(sender2, 0, seq3, 0); + QObject::disconnect(sender2, 0, &seq4, 0); + seq1.reset(); + seq2.reset(); + seq3->reset(); + seq4.reset(); + + // try 3 + connect(sender2, SIGNAL(signal1()), &seq1, SLOT(slot1())); + connect(sender2, SIGNAL(signal1()), &seq2, SLOT(slot1_disconnectNext())); + connect(sender2, SIGNAL(signal1()), seq3, SLOT(slot1())); + connect(sender2, SIGNAL(signal1()), &seq4, SLOT(slot1())); + connect(sender2, SIGNAL(signal1()), &seq1, SLOT(slot2())); + connect(sender2, SIGNAL(signal1()), &seq2, SLOT(slot2_reconnectNext())); + connect(sender2, SIGNAL(signal1()), seq3, SLOT(slot2())); + connect(sender2, SIGNAL(signal1()), &seq4, SLOT(slot2())); + + SequenceObject::sequence = sequence = 0; + sender2->emitSignal1(); + QCOMPARE(seq1.called(1), true); + QCOMPARE(seq2.called(1), true); + QCOMPARE(seq3->called(1), false); + QCOMPARE(seq4.called(1), true); + QCOMPARE(seq1.called(2), true); + QCOMPARE(seq2.called(2), true); + QCOMPARE(seq3->called(2), false); + QCOMPARE(seq4.called(2), true); + QCOMPARE(seq1.sequence_slot1, ++sequence); + QCOMPARE(seq2.sequence_slot1, ++sequence); + QCOMPARE(seq4.sequence_slot1, ++sequence); + QCOMPARE(seq1.sequence_slot2, ++sequence); + QCOMPARE(seq2.sequence_slot2, ++sequence); + QCOMPARE(seq4.sequence_slot2, ++sequence); + + // ensure emission order even if objects are destroyed during emission + QObject::disconnect(sender2, 0, &seq1, 0); + QObject::disconnect(sender2, 0, &seq2, 0); + QObject::disconnect(sender2, 0, seq3, 0); + QObject::disconnect(sender2, 0, &seq4, 0); + seq1.reset(); + seq2.reset(); + seq3->reset(); + seq4.reset(); + + connect(sender2, SIGNAL(signal1()), &seq1, SLOT(slot1())); + connect(sender2, SIGNAL(signal1()), &seq2, SLOT(slot1_deleteNext())); + connect(sender2, SIGNAL(signal1()), seq3, SLOT(slot1())); + connect(sender2, SIGNAL(signal1()), &seq4, SLOT(slot1())); + connect(sender2, SIGNAL(signal1()), &seq1, SLOT(slot2())); + connect(sender2, SIGNAL(signal1()), &seq2, SLOT(slot2_deleteSender())); + connect(sender2, SIGNAL(signal1()), seq3, SLOT(slot2())); + connect(sender2, SIGNAL(signal1()), &seq4, SLOT(slot2())); + + QPointer psender = sender2; + QPointer pseq3 = seq3; + + SequenceObject::sequence = sequence = 0; + sender2->emitSignal1(); + QCOMPARE(static_cast(psender), static_cast(0)); + QCOMPARE(static_cast(pseq3), static_cast(0)); + QCOMPARE(seq1.called(1), true); + QCOMPARE(seq2.called(1), true); + QCOMPARE(seq4.called(1), true); + QCOMPARE(seq1.called(2), true); + QCOMPARE(seq2.called(2), true); + QCOMPARE(seq4.called(2), false); + QCOMPARE(seq1.sequence_slot1, ++sequence); + QCOMPARE(seq2.sequence_slot1, ++sequence); + QCOMPARE(seq4.sequence_slot1, ++sequence); + QCOMPARE(seq1.sequence_slot2, ++sequence); + QCOMPARE(seq2.sequence_slot2, ++sequence); + + QPointer psender3 = new SenderObject; + connect(psender3, SIGNAL(signal1()), psender3, SIGNAL(signal2())); + connect(psender3, SIGNAL(signal2()), &seq1, SLOT(slot2_deleteSender())); + psender3->emitSignal1(); + QVERIFY(!psender3); +} + +static int instanceCount = 0; + +struct CustomType +{ + CustomType(int l1 = 0, int l2 = 0, int l3 = 0): i1(l1), i2(l2), i3(l3) + { ++instanceCount; } + CustomType(const CustomType &other): i1(other.i1), i2(other.i2), i3(other.i3) + { ++instanceCount; } + ~CustomType() { --instanceCount; } + + int i1, i2, i3; + int value() { return i1 + i2 + i3; } +}; + +Q_DECLARE_METATYPE(CustomType*) + +class QCustomTypeChecker: public QObject +{ + Q_OBJECT + +public: + QCustomTypeChecker(QObject *parent = 0): QObject(parent) {} + void doEmit(CustomType ct) + { emit signal1(ct); } + +public slots: + void slot1(CustomType ct); + +signals: + void signal1(CustomType ct); + +public: + CustomType received; +}; + +void QCustomTypeChecker::slot1(CustomType ct) +{ received = ct; } + + +void tst_QObject::customTypes() +{ + CustomType t0; + CustomType t1(1, 2, 3); + CustomType t2(2, 3, 4); + + { + QCustomTypeChecker checker; + QCOMPARE(instanceCount, 4); + + connect(&checker, SIGNAL(signal1(CustomType)), &checker, SLOT(slot1(CustomType)), + Qt::DirectConnection); + QCOMPARE(checker.received.value(), 0); + checker.doEmit(t1); + QCOMPARE(checker.received.value(), t1.value()); + checker.received = t0; + + int idx = qRegisterMetaType("CustomType"); + 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"), 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"); + qRegisterMetaTypeStreamOperators("CustomType"); + + { + CustomType t1(1, 2, 3); + QCOMPARE(instanceCount, 1); + QDataStream stream(&ba, (QIODevice::OpenMode)QIODevice::WriteOnly); + QMetaType::save(stream, idx, &t1); + } + + QCOMPARE(instanceCount, 0); + + { + CustomType t2; + QCOMPARE(instanceCount, 1); + QDataStream stream(&ba, (QIODevice::OpenMode)QIODevice::ReadOnly); + QMetaType::load(stream, idx, &t2); + QCOMPARE(instanceCount, 1); + QCOMPARE(t2.i1, 1); + QCOMPARE(t2.i2, 2); + QCOMPARE(t2.i3, 3); + } + QCOMPARE(instanceCount, 0); +} + +typedef QString CustomString; + +class PropertyObject : public QObject +{ + Q_OBJECT + Q_ENUMS(Alpha Priority) + + Q_PROPERTY(Alpha alpha READ alpha WRITE setAlpha) + Q_PROPERTY(Priority priority READ priority WRITE setPriority) + Q_PROPERTY(int number READ number WRITE setNumber) + Q_PROPERTY(QString string READ string WRITE setString) + Q_PROPERTY(QVariant variant READ variant WRITE setVariant) + Q_PROPERTY(CustomType* custom READ custom WRITE setCustom) + Q_PROPERTY(float myFloat READ myFloat WRITE setMyFloat) + Q_PROPERTY(qreal myQReal READ myQReal WRITE setMyQReal) + Q_PROPERTY(CustomString customString READ customString WRITE setCustomString ) + +public: + enum Alpha { + Alpha0, + Alpha1, + Alpha2 + }; + + enum Priority { High, Low, VeryHigh, VeryLow }; + + PropertyObject() + : m_alpha(Alpha0), m_priority(High), m_number(0), m_custom(0), m_float(42) + {} + + Alpha alpha() const { return m_alpha; } + void setAlpha(Alpha alpha) { m_alpha = alpha; } + + Priority priority() const { return m_priority; } + void setPriority(Priority priority) { m_priority = priority; } + + int number() const { return m_number; } + void setNumber(int number) { m_number = number; } + + QString string() const { return m_string; } + void setString(const QString &string) { m_string = string; } + + QVariant variant() const { return m_variant; } + void setVariant(const QVariant &variant) { m_variant = variant; } + + CustomType *custom() const { return m_custom; } + void setCustom(CustomType *custom) { m_custom = custom; } + + void setMyFloat(float value) { m_float = value; } + inline float myFloat() const { return m_float; } + + void setMyQReal(qreal value) { m_qreal = value; } + qreal myQReal() const { return m_qreal; } + + CustomString customString() const { return m_customString; } + void setCustomString(const QString &string) { m_customString = string; } + +private: + Alpha m_alpha; + Priority m_priority; + int m_number; + QString m_string; + QVariant m_variant; + CustomType *m_custom; + float m_float; + qreal m_qreal; + CustomString m_customString; +}; + +Q_DECLARE_METATYPE(PropertyObject::Priority) + +void tst_QObject::threadSignalEmissionCrash() +{ + 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() != 0); + QCOMPARE(object.thread(), currentThread); + // children inherit their parent's thread + QObject child(&object); + QCOMPARE(child.thread(), object.thread()); + } + + QObject *object = 0; + QObject *child = 0; + + { + TestThread thr; + QVERIFY(thr.thread() != 0); + QCOMPARE(thr.thread(), currentThread); + + thr.object = &object; + thr.child = &child; + + thr.mutex.lock(); + thr.start(); + thr.cond.wait(&thr.mutex); + + // thread affinity for an object with no parent should be the + // thread in which the object was created + QCOMPARE(object->thread(), (QThread *)&thr); + // children inherit their parent's thread + QCOMPARE(child->thread(), object->thread()); + + thr.cond.wakeOne(); + thr.mutex.unlock(); + thr.wait(); + + // even though the thread is no longer running, the affinity + // should not change + QCOMPARE(object->thread(), (QThread *)&thr); + QCOMPARE(child->thread(), object->thread()); + } + + // the thread has been destroyed, thread affinity should + // automatically reset to no thread + QCOMPARE(object->thread(), (QThread *)0); + QCOMPARE(child->thread(), object->thread()); + + delete object; +} + +class MoveToThreadObject : public QObject +{ + Q_OBJECT +public: + QThread *timerEventThread; + QThread *customEventThread; + QThread *slotThread; + + MoveToThreadObject(QObject *parent = 0) + : QObject(parent), timerEventThread(0), customEventThread(0), slotThread(0) + { } + + void customEvent(QEvent *) + { + if (customEventThread) + qFatal("%s: customEventThread should be null", Q_FUNC_INFO); + customEventThread = QThread::currentThread(); + emit theSignal(); + } + + void timerEvent(QTimerEvent *) + { + if (timerEventThread) + qFatal("%s: timerEventThread should be null", Q_FUNC_INFO); + timerEventThread = QThread::currentThread(); + emit theSignal(); + } + +public slots: + void theSlot() + { + if (slotThread) + qFatal("%s: slotThread should be null", Q_FUNC_INFO); + slotThread = QThread::currentThread(); + emit theSignal(); + } + +signals: + void theSignal(); +}; + +class MoveToThreadThread : public QThread +{ +public: + ~MoveToThreadThread() + { + if (isRunning()) { + terminate(); + wait(); + } + } + void start() + { + QEventLoop eventLoop; + connect(this, SIGNAL(started()), &eventLoop, SLOT(quit()), Qt::QueuedConnection); + QThread::start(); + // wait for thread to start + (void) eventLoop.exec(); + } + void run() + { (void) exec(); } +}; + +void tst_QObject::moveToThread() +{ + QThread *currentThread = QThread::currentThread(); + + { + QObject *object = new QObject; + QObject *child = new QObject(object); + QThread* otherthread = new QThread(); + QVERIFY(otherthread != object->thread()); + QCOMPARE(object->thread(), currentThread); + QCOMPARE(child->thread(), currentThread); + object->moveToThread(otherthread); + QCOMPARE(object->thread(), otherthread); + QCOMPARE(child->thread(), otherthread); + delete object; + delete otherthread; + } + + { + MoveToThreadThread thread; + thread.start(); + + QObject *object = new QObject; + QObject *child = new QObject(object); + QPointer opointer = object; + QPointer cpointer = object; + + QCOMPARE(object->thread(), currentThread); + QCOMPARE(child->thread(), currentThread); + object->moveToThread(&thread); + QCOMPARE(object->thread(), (QThread *)&thread); + QCOMPARE(child->thread(), (QThread *)&thread); + + connect(object, SIGNAL(destroyed()), &thread, SLOT(quit()), Qt::DirectConnection); + QMetaObject::invokeMethod(object, "deleteLater", Qt::QueuedConnection); + thread.wait(); + + QVERIFY(opointer == 0); + QVERIFY(cpointer == 0); + } + + { + // make sure posted events are moved with the object + MoveToThreadThread thread; + thread.start(); + + MoveToThreadObject *object = new MoveToThreadObject; + MoveToThreadObject *child = new MoveToThreadObject(object); + + connect(object, SIGNAL(theSignal()), &thread, SLOT(quit()), Qt::DirectConnection); + QCoreApplication::postEvent(child, new QEvent(QEvent::User)); + QCoreApplication::postEvent(object, new QEvent(QEvent::User)); + + QCOMPARE(object->thread(), currentThread); + QCOMPARE(child->thread(), currentThread); + object->moveToThread(&thread); + QCOMPARE(object->thread(), (QThread *)&thread); + QCOMPARE(child->thread(), (QThread *)&thread); + + thread.wait(); + + QCOMPARE(object->customEventThread, (QThread *)&thread); + QCOMPARE(child->customEventThread, (QThread *)&thread); + + thread.start(); + connect(object, SIGNAL(destroyed()), &thread, SLOT(quit()), Qt::DirectConnection); + QMetaObject::invokeMethod(object, "deleteLater", Qt::QueuedConnection); + thread.wait(); + } + + { + // make sure timers are moved with the object + MoveToThreadThread thread; + thread.start(); + + MoveToThreadObject *object = new MoveToThreadObject; + MoveToThreadObject *child = new MoveToThreadObject(object); + + connect(object, SIGNAL(theSignal()), &thread, SLOT(quit()), Qt::DirectConnection); + + 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(); + } + + { + // make sure socket notifiers are moved with the object + MoveToThreadThread thread; + thread.start(); + + QTcpServer server; + QVERIFY(server.listen(QHostAddress::LocalHost, 0)); + QTcpSocket *socket = new QTcpSocket; + MoveToThreadObject *child = new MoveToThreadObject(socket); + connect(socket, SIGNAL(disconnected()), child, SLOT(theSlot()), Qt::DirectConnection); + connect(child, SIGNAL(theSignal()), &thread, SLOT(quit()), Qt::DirectConnection); + + socket->connectToHost(server.serverAddress(), server.serverPort()); + + QVERIFY(server.waitForNewConnection(1000)); + QTcpSocket *serverSocket = server.nextPendingConnection(); + QVERIFY(serverSocket); + + socket->waitForConnected(); + + QCOMPARE(socket->thread(), currentThread); + socket->moveToThread(&thread); + QCOMPARE(socket->thread(), (QThread *)&thread); + + serverSocket->close(); + + QVERIFY(thread.wait(10000)); + + QCOMPARE(child->slotThread, (QThread *)&thread); + + thread.start(); + connect(socket, SIGNAL(destroyed()), &thread, SLOT(quit()), Qt::DirectConnection); + QMetaObject::invokeMethod(socket, "deleteLater", Qt::QueuedConnection); + thread.wait(); + } +} + + +void tst_QObject::property() +{ + PropertyObject object; + const QMetaObject *mo = object.metaObject(); + QMetaProperty property; + QVERIFY(mo); + + QVERIFY(mo->indexOfProperty("alpha") != -1); + property = mo->property(mo->indexOfProperty("alpha")); + QVERIFY(property.isEnumType()); + QCOMPARE(property.typeName(), "Alpha"); + QCOMPARE(property.type(), QVariant::Int); + + QVariant var = object.property("alpha"); + QVERIFY(!var.isNull()); + QCOMPARE(var.toInt(), int(PropertyObject::Alpha0)); + object.setAlpha(PropertyObject::Alpha1); + QCOMPARE(object.property("alpha").toInt(), int(PropertyObject::Alpha1)); + QVERIFY(object.setProperty("alpha", PropertyObject::Alpha2)); + QCOMPARE(object.property("alpha").toInt(), int(PropertyObject::Alpha2)); + QVERIFY(object.setProperty("alpha", "Alpha1")); + QCOMPARE(object.property("alpha").toInt(), int(PropertyObject::Alpha1)); + QVERIFY(!object.setProperty("alpha", QVariant())); + + QVERIFY(mo->indexOfProperty("number") != -1); + QCOMPARE(object.property("number").toInt(), 0); + object.setNumber(24); + QCOMPARE(object.property("number"), QVariant(24)); + QVERIFY(object.setProperty("number", 12)); + QCOMPARE(object.property("number"), QVariant(12)); + QVERIFY(object.setProperty("number", "42")); + QCOMPARE(object.property("number"), QVariant(42)); + + QVERIFY(mo->indexOfProperty("string") != -1); + QCOMPARE(object.property("string").toString(), QString()); + object.setString("String1"); + QCOMPARE(object.property("string"), QVariant("String1")); + QVERIFY(object.setProperty("string", "String2")); + QCOMPARE(object.property("string"), QVariant("String2")); + QVERIFY(!object.setProperty("string", QVariant())); + + const int idx = mo->indexOfProperty("variant"); + QVERIFY(idx != -1); + QVERIFY(mo->property(idx).type() == QVariant::LastType); + QCOMPARE(object.property("variant"), QVariant()); + QVariant variant1(42); + QVariant variant2("string"); + object.setVariant(variant1); + QCOMPARE(object.property("variant"), variant1); + QVERIFY(object.setProperty("variant", variant2)); + QCOMPARE(object.variant(), QVariant(variant2)); + QCOMPARE(object.property("variant"), variant2); + QVERIFY(object.setProperty("variant", QVariant())); + QCOMPARE(object.property("variant"), QVariant()); + + QVERIFY(mo->indexOfProperty("custom") != -1); + property = mo->property(mo->indexOfProperty("custom")); + QVERIFY(property.isValid()); + QVERIFY(property.isWritable()); + QVERIFY(!property.isEnumType()); + QCOMPARE(property.typeName(), "CustomType*"); + QCOMPARE(property.type(), QVariant::UserType); + + CustomType *customPointer = 0; + QVariant customVariant = object.property("custom"); + customPointer = qvariant_cast(customVariant); + QCOMPARE(customPointer, object.custom()); + + CustomType custom; + customPointer = &custom; + qVariantSetValue(customVariant, customPointer); + + property = mo->property(mo->indexOfProperty("custom")); + QVERIFY(property.isWritable()); + QCOMPARE(property.typeName(), "CustomType*"); + QCOMPARE(property.type(), QVariant::UserType); + + QVERIFY(object.setProperty("custom", customVariant)); + QCOMPARE(object.custom(), customPointer); + + customVariant = object.property("custom"); + customPointer = qvariant_cast(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"); + + 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()); + QCOMPARE(qvariant_cast(var), PropertyObject::High); + object.setPriority(PropertyObject::Low); + QCOMPARE(qvariant_cast(object.property("priority")), PropertyObject::Low); + QVERIFY(object.setProperty("priority", PropertyObject::VeryHigh)); + QCOMPARE(qvariant_cast(object.property("priority")), PropertyObject::VeryHigh); + QVERIFY(object.setProperty("priority", "High")); + QCOMPARE(qvariant_cast(object.property("priority")), PropertyObject::High); + QVERIFY(!object.setProperty("priority", QVariant())); + + var = object.property("priority"); + QCOMPARE(qvariant_cast(var), PropertyObject::High); + object.setPriority(PropertyObject::Low); + QCOMPARE(qvariant_cast(object.property("priority")), PropertyObject::Low); + object.setProperty("priority", var); + QCOMPARE(qvariant_cast(object.property("priority")), PropertyObject::High); + + qRegisterMetaType("CustomString"); + QVERIFY(mo->indexOfProperty("customString") != -1); + QCOMPARE(object.property("customString").toString(), QString()); + object.setCustomString("String1"); + QCOMPARE(object.property("customString"), QVariant("String1")); + QVERIFY(object.setProperty("customString", "String2")); + QCOMPARE(object.property("customString"), QVariant("String2")); + QVERIFY(!object.setProperty("customString", QVariant())); +} + +void tst_QObject::metamethod() +{ + SenderObject obj; + const QMetaObject *mobj = obj.metaObject(); + QMetaMethod m; + + m = mobj->method(mobj->indexOfMethod("invoke1()")); + QVERIFY(QByteArray(m.signature()) == "invoke1()"); + QVERIFY(m.methodType() == QMetaMethod::Method); + QVERIFY(m.access() == QMetaMethod::Public); + QVERIFY(!(m.attributes() & QMetaMethod::Scriptable)); + + m = mobj->method(mobj->indexOfMethod("sinvoke1()")); + QVERIFY(QByteArray(m.signature()) == "sinvoke1()"); + QVERIFY(m.methodType() == QMetaMethod::Method); + QVERIFY(m.access() == QMetaMethod::Public); + QVERIFY((m.attributes() & QMetaMethod::Scriptable)); + + m = mobj->method(mobj->indexOfMethod("invoke2()")); + QVERIFY(QByteArray(m.signature()) == "invoke2()"); + QVERIFY(m.methodType() == QMetaMethod::Method); + QVERIFY(m.access() == QMetaMethod::Protected); + QVERIFY(!(m.attributes() & QMetaMethod::Scriptable)); + + m = mobj->method(mobj->indexOfMethod("sinvoke2()")); + QVERIFY(QByteArray(m.signature()) == "sinvoke2()"); + QVERIFY(m.methodType() == QMetaMethod::Method); + QVERIFY(m.access() == QMetaMethod::Protected); + QVERIFY((m.attributes() & QMetaMethod::Scriptable)); + + m = mobj->method(mobj->indexOfMethod("invoke3()")); + QVERIFY(QByteArray(m.signature()) == "invoke3()"); + QVERIFY(m.methodType() == QMetaMethod::Method); + QVERIFY(m.access() == QMetaMethod::Private); + QVERIFY(!(m.attributes() & QMetaMethod::Scriptable)); + + m = mobj->method(mobj->indexOfMethod("sinvoke3()")); + QVERIFY(QByteArray(m.signature()) == "sinvoke3()"); + QVERIFY(m.methodType() == QMetaMethod::Method); + QVERIFY(m.access() == QMetaMethod::Private); + QVERIFY((m.attributes() & QMetaMethod::Scriptable)); + + m = mobj->method(mobj->indexOfMethod("signal5()")); + QVERIFY(QByteArray(m.signature()) == "signal5()"); + QVERIFY(m.methodType() == QMetaMethod::Signal); + QVERIFY(m.access() == QMetaMethod::Protected); + QVERIFY(!(m.attributes() & QMetaMethod::Scriptable)); + + m = mobj->method(mobj->indexOfMethod("aPublicSlot()")); + QVERIFY(QByteArray(m.signature()) == "aPublicSlot()"); + QVERIFY(m.methodType() == QMetaMethod::Slot); + QVERIFY(m.access() == QMetaMethod::Public); + QVERIFY(!(m.attributes() & QMetaMethod::Scriptable)); + + m = mobj->method(mobj->indexOfMethod("invoke1()")); + QCOMPARE(m.parameterNames().count(), 0); + QCOMPARE(m.parameterTypes().count(), 0); + + m = mobj->method(mobj->indexOfMethod("invoke2(int)")); + QCOMPARE(m.parameterNames().count(), 1); + QCOMPARE(m.parameterTypes().count(), 1); + QCOMPARE(m.parameterTypes().at(0), QByteArray("int")); + QVERIFY(m.parameterNames().at(0).isEmpty()); + + m = mobj->method(mobj->indexOfMethod("invoke3(int,int)")); + QCOMPARE(m.parameterNames().count(), 2); + QCOMPARE(m.parameterTypes().count(), 2); + QCOMPARE(m.parameterTypes().at(0), QByteArray("int")); + QCOMPARE(m.parameterNames().at(0), QByteArray("hinz")); + QCOMPARE(m.parameterTypes().at(1), QByteArray("int")); + QCOMPARE(m.parameterNames().at(1), QByteArray("kunz")); + +} + +namespace QObjectTest +{ + class TestObject: public QObject + { + Q_OBJECT + public: + TestObject(): QObject(), i(0) {} + void doEmit() { emit aSignal(); } + int i; + public slots: + void aSlot() { ++i; } + signals: + void aSignal(); + }; +} + +void tst_QObject::namespaces() +{ + QObjectTest::TestObject obj; + + QVERIFY(connect(&obj, SIGNAL(aSignal()), &obj, SLOT(aSlot()))); + obj.doEmit(); + QCOMPARE(obj.i, 1); +} + +class SuperObject : public QObject +{ + Q_OBJECT +public: + QObject *theSender; + int theSignalId; + + SuperObject() + { + theSender = 0; + theSignalId = 0; + } + + friend class tst_QObject; + + using QObject::sender; + +public slots: + void rememberSender() + { + theSender = sender(); + theSignalId = senderSignalIndex(); + } + + void deleteAndRememberSender() + { + delete theSender; + rememberSender(); + } +signals: + void anotherSignal(); + void theSignal(); +}; + +void tst_QObject::sender() +{ + { + SuperObject sender; + SuperObject receiver; + connect(&sender, SIGNAL(anotherSignal()), + &receiver, SLOT(rememberSender())); + connect(&sender, SIGNAL(theSignal()), + &receiver, SLOT(rememberSender())); + QCOMPARE(receiver.sender(), (QObject *)0); + QCOMPARE(receiver.senderSignalIndex(), -1); + emit sender.theSignal(); + QCOMPARE(receiver.theSender, (QObject *)&sender); + QCOMPARE(receiver.sender(), (QObject *)0); + QCOMPARE(receiver.theSignalId, + sender.metaObject()->indexOfSignal("theSignal()")); + QCOMPARE(receiver.senderSignalIndex(), -1); + + emit sender.anotherSignal(); + QCOMPARE(receiver.theSignalId, + sender.metaObject()->indexOfSignal("anotherSignal()")); + QCOMPARE(receiver.senderSignalIndex(), -1); + } + + { + SuperObject *sender = new SuperObject; + SuperObject *receiver = new SuperObject; + connect(sender, SIGNAL(theSignal()), + receiver, SLOT(rememberSender()), + Qt::BlockingQueuedConnection); + + QThread thread; + receiver->moveToThread(&thread); + connect(sender, SIGNAL(theSignal()), + &thread, SLOT(quit()), + Qt::DirectConnection); + + QCOMPARE(receiver->sender(), (QObject *)0); + QCOMPARE(receiver->senderSignalIndex(), -1); + receiver->theSender = 0; + receiver->theSignalId = -1; + thread.start(); + emit sender->theSignal(); + QCOMPARE(receiver->theSender, (QObject *) sender); + QCOMPARE(receiver->sender(), (QObject *)0); + QCOMPARE(receiver->theSignalId, + sender->metaObject()->indexOfSignal("theSignal()")); + QCOMPARE(receiver->senderSignalIndex(), -1); + + QVERIFY(thread.wait(10000)); + delete receiver; + delete sender; + } + + { + SuperObject *sender = new SuperObject; + SuperObject receiver; + connect(sender, SIGNAL(theSignal()), + &receiver, SLOT(deleteAndRememberSender())); + QCOMPARE(receiver.sender(), (QObject *)0); + receiver.theSender = sender; + emit sender->theSignal(); + QCOMPARE(receiver.theSender, (QObject *)0); + QCOMPARE(receiver.sender(), (QObject *)0); + } + + { + SuperObject *sender = new SuperObject; + SuperObject *receiver = new SuperObject; + connect(sender, SIGNAL(theSignal()), + receiver, SLOT(deleteAndRememberSender()), + Qt::BlockingQueuedConnection); + + QThread thread; + receiver->moveToThread(&thread); + connect(sender, SIGNAL(destroyed()), + &thread, SLOT(quit()), + Qt::DirectConnection); + + QCOMPARE(receiver->sender(), (QObject *)0); + receiver->theSender = sender; + thread.start(); + emit sender->theSignal(); + QCOMPARE(receiver->theSender, (QObject *)0); + QCOMPARE(receiver->sender(), (QObject *)0); + + QVERIFY(thread.wait(10000)); + delete receiver; + } +} + +namespace Foo +{ + struct Bar + { + virtual ~Bar() {} + virtual int rtti() const = 0; + }; + + struct Bleh + { + virtual ~Bleh() {} + virtual int rtti() const = 0; + }; +} + +Q_DECLARE_INTERFACE(Foo::Bar, "com.qtest.foobar") + +#define Bleh_iid "com.qtest.bleh" +Q_DECLARE_INTERFACE(Foo::Bleh, Bleh_iid) + +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(&obj); + QVERIFY(bar); + QCOMPARE(bar->rtti(), 42); + QCOMPARE(static_cast(&obj), bar); + + BlehObject bleh; + + bar = qobject_cast(&bleh); + QVERIFY(!bar); + Foo::Bleh *b = qobject_cast(&bleh); + QCOMPARE(b->rtti(), 43); + QCOMPARE(static_cast(&bleh), b); + +} + +class DestroyedListener : public QObject +{ + Q_OBJECT +public: + inline DestroyedListener() : pointerWasZero(false) {} + + QPointer 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 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 &ref); + void constTypeRefSignal(const Template &ref); + void typeConstRefSignal(Template const &ref); + + void typePointerConstRefSignal(Class * const &); + + void constTemplateSignal1( Template ); + 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 &) {} + void constTypeRefSlot(const Template &) {} + void typeConstRefSlot(Template const &) {} + + void typePointerConstRefSlot(Class * const &) {} + + void constTemplateSlot1(Template const) {} + void constTemplateSlot2(const Template ) {} + 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 const &', + // 'const T &', and 'T' + + QVERIFY(object.connect(&object, + SIGNAL(typeRefSignal(Template &)), + SLOT(typeRefSlot(Template &)))); + + QVERIFY(object.connect(&object, + SIGNAL(constTypeRefSignal(const Template &)), + SLOT(constTypeRefSlot(const Template &)))); + QVERIFY(object.connect(&object, + SIGNAL(constTypeRefSignal(const Template &)), + SLOT(constTypeRefSlot(const Template &)))); + QVERIFY(object.connect(&object, + SIGNAL(constTypeRefSignal(const Template &)), + SLOT(constTypeRefSlot(Template const &)))); + QVERIFY(object.connect(&object, + SIGNAL(constTypeRefSignal(Template const &)), + SLOT(constTypeRefSlot(Template const &)))); + QVERIFY(object.connect(&object, + SIGNAL(constTypeRefSignal(Template const &)), + SLOT(constTypeRefSlot(Template const &)))); + + QVERIFY(object.connect(&object, + SIGNAL(constTypeRefSignal(const Template &)), + SLOT(typeConstRefSlot(const Template &)))); + QVERIFY(object.connect(&object, + SIGNAL(constTypeRefSignal(const Template &)), + SLOT(typeConstRefSlot(const Template &)))); + QVERIFY(object.connect(&object, + SIGNAL(constTypeRefSignal(const Template &)), + SLOT(typeConstRefSlot(Template const &)))); + QVERIFY(object.connect(&object, + SIGNAL(constTypeRefSignal(Template const &)), + SLOT(typeConstRefSlot(Template const &)))); + QVERIFY(object.connect(&object, + SIGNAL(constTypeRefSignal(Template const &)), + SLOT(typeConstRefSlot(Template const &)))); + + QVERIFY(object.connect(&object, + SIGNAL(typeConstRefSignal(const Template &)), + SLOT(constTypeRefSlot(const Template &)))); + QVERIFY(object.connect(&object, + SIGNAL(typeConstRefSignal(const Template &)), + SLOT(constTypeRefSlot(const Template &)))); + QVERIFY(object.connect(&object, + SIGNAL(typeConstRefSignal(const Template &)), + SLOT(constTypeRefSlot(Template const &)))); + QVERIFY(object.connect(&object, + SIGNAL(typeConstRefSignal(Template const &)), + SLOT(constTypeRefSlot(Template const &)))); + QVERIFY(object.connect(&object, + SIGNAL(typeConstRefSignal(Template const &)), + SLOT(constTypeRefSlot(Template const &)))); + + QVERIFY(object.connect(&object, + SIGNAL(typeConstRefSignal(const Template &)), + SLOT(typeConstRefSlot(const Template &)))); + QVERIFY(object.connect(&object, + SIGNAL(typeConstRefSignal(const Template &)), + SLOT(typeConstRefSlot(const Template &)))); + QVERIFY(object.connect(&object, + SIGNAL(typeConstRefSignal(const Template &)), + SLOT(typeConstRefSlot(Template const &)))); + QVERIFY(object.connect(&object, + SIGNAL(typeConstRefSignal(Template const &)), + SLOT(typeConstRefSlot(Template const &)))); + QVERIFY(object.connect(&object, + SIGNAL(typeConstRefSignal(Template const &)), + SLOT(typeConstRefSlot(Template const &)))); +} + +class SiblingDeleter : public QObject +{ +public: + inline SiblingDeleter(QObject *sibling, QObject *parent) + : QObject(parent), sibling(sibling) {} + inline virtual ~SiblingDeleter() { delete sibling; } + +private: + QPointer sibling; +}; + + +void tst_QObject::childDeletesItsSibling() +{ + QObject *commonParent = new QObject(0); + QPointer child = new QObject(0); + QPointer siblingDeleter = new SiblingDeleter(child, commonParent); + child->setParent(commonParent); + delete commonParent; // don't crash + QVERIFY(!child); + QVERIFY(!siblingDeleter); +} + +void tst_QObject::floatProperty() +{ + PropertyObject obj; + const int idx = obj.metaObject()->indexOfProperty("myFloat"); + QVERIFY(idx > 0); + QMetaProperty prop = obj.metaObject()->property(idx); + QVERIFY(prop.isValid()); + QVERIFY(prop.type() == uint(QMetaType::type("float"))); + QVERIFY(!prop.write(&obj, QVariant("Hello"))); + QVERIFY(prop.write(&obj, qVariantFromValue(128.0f))); + QVariant v = prop.read(&obj); + QVERIFY(int(v.userType()) == QMetaType::Float); + QVERIFY(qvariant_cast(v) == 128.0f); +} + +void tst_QObject::qrealProperty() +{ + PropertyObject obj; + const int idx = obj.metaObject()->indexOfProperty("myQReal"); + QVERIFY(idx > 0); + QMetaProperty prop = obj.metaObject()->property(idx); + QVERIFY(prop.isValid()); + QVERIFY(prop.type() == uint(QMetaType::type("qreal"))); + QVERIFY(!prop.write(&obj, QVariant("Hello"))); + + QVERIFY(prop.write(&obj, qVariantFromValue(128.0f))); + QVariant v = prop.read(&obj); + QCOMPARE(v.userType(), qMetaTypeId()); + QVERIFY(qvariant_cast(v) == 128.0); + + QVERIFY(prop.write(&obj, qVariantFromValue(double(127)))); + v = prop.read(&obj); + QCOMPARE(v.userType(), qMetaTypeId()); + QVERIFY(qvariant_cast(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(e)->propertyName()); + } + return QObject::event(e); + } + + QList changedDynamicProperties; +}; + +void tst_QObject::dynamicProperties() +{ + DynamicPropertyObject obj; + + QVERIFY(obj.dynamicPropertyNames().isEmpty()); + + QVERIFY(obj.setProperty("number", 42)); + QVERIFY(obj.changedDynamicProperties.isEmpty()); + QCOMPARE(obj.property("number").toInt(), 42); + + QVERIFY(!obj.setProperty("number", "invalid string")); + QVERIFY(obj.changedDynamicProperties.isEmpty()); + + QVERIFY(!obj.setProperty("myuserproperty", "Hello")); + QCOMPARE(obj.changedDynamicProperties.count(), 1); + QCOMPARE(obj.changedDynamicProperties.first(), QByteArray("myuserproperty")); + obj.changedDynamicProperties.clear(); + + QCOMPARE(obj.property("myuserproperty").toString(), QString("Hello")); + + QCOMPARE(obj.dynamicPropertyNames().count(), 1); + QCOMPARE(obj.dynamicPropertyNames().first(), QByteArray("myuserproperty")); + + QVERIFY(!obj.setProperty("myuserproperty", QVariant())); + + QCOMPARE(obj.changedDynamicProperties.count(), 1); + QCOMPARE(obj.changedDynamicProperties.first(), QByteArray("myuserproperty")); + obj.changedDynamicProperties.clear(); + + QVERIFY(obj.property("myuserproperty").isNull()); + + QVERIFY(obj.dynamicPropertyNames().isEmpty()); +} + +void tst_QObject::recursiveSignalEmission() +{ + QProcess proc; + proc.start("./signalbug"); + QVERIFY(proc.waitForFinished()); + QVERIFY(proc.exitStatus() == QProcess::NormalExit); + QCOMPARE(proc.exitCode(), 0); +} + +void tst_QObject::blockingQueuedConnection() +{ + { + SenderObject sender; + + MoveToThreadThread thread; + ReceiverObject receiver; + receiver.moveToThread(&thread); + thread.start(); + + receiver.connect(&sender, SIGNAL(signal1()), SLOT(slot1()), Qt::BlockingQueuedConnection); + sender.emitSignal1(); + QVERIFY(receiver.called(1)); + + receiver.reset(); + QVERIFY(QMetaObject::invokeMethod(&receiver, "slot1", Qt::BlockingQueuedConnection)); + QVERIFY(receiver.called(1)); + + thread.quit(); + QVERIFY(thread.wait()); + } +} + +class EventSpy : public QObject +{ + Q_OBJECT + +public: + typedef QList > EventList; + + EventSpy(QObject *parent = 0) + : QObject(parent) + { } + + EventList eventList() + { + return events; + } + + void clear() + { + events.clear(); + } + + bool eventFilter(QObject *object, QEvent *event) + { + events.append(qMakePair(object, event->type())); + return false; + } + +private: + EventList events; +}; + +void tst_QObject::compatibilityChildInsertedEvents() +{ + EventSpy::EventList expected; + + { + // no children created, so we expect no events + QObject object; + EventSpy spy; + object.installEventFilter(&spy); + + QCoreApplication::postEvent(&object, new QEvent(QEvent::Type(QEvent::User + 1))); + + QCoreApplication::processEvents(); + + expected = + EventSpy::EventList() + << qMakePair(&object, QEvent::Type(QEvent::User + 1)); + QCOMPARE(spy.eventList(), expected); + } + + { + // 2 children, so we expect 2 ChildAdded and 2 ChildInserted events + QObject object; + EventSpy spy; + object.installEventFilter(&spy); + + QCoreApplication::postEvent(&object, new QEvent(QEvent::Type(QEvent::User + 1))); + + QObject child1(&object); + QObject child2; + child2.setParent(&object); + + QCoreApplication::postEvent(&object, new QEvent(QEvent::Type(QEvent::User + 2))); + + expected = + EventSpy::EventList() + << qMakePair(&object, QEvent::ChildAdded) + << qMakePair(&object, QEvent::ChildAdded); + QCOMPARE(spy.eventList(), expected); + spy.clear(); + + QCoreApplication::processEvents(); + + expected = + EventSpy::EventList() + << qMakePair(&object, QEvent::Type(QEvent::User + 1)) + << qMakePair(&object, QEvent::Type(QEvent::User + 2)); + QCOMPARE(spy.eventList(), expected); + } + + { + // 2 children, but one is reparented away, so we expect: + // 2 ChildAdded, 1 ChildRemoved, and 1 ChildInserted + QObject object; + EventSpy spy; + object.installEventFilter(&spy); + + QCoreApplication::postEvent(&object, new QEvent(QEvent::Type(QEvent::User + 1))); + + QObject child1(&object); + QObject child2; + child2.setParent(&object); + child2.setParent(0); + + QCoreApplication::postEvent(&object, new QEvent(QEvent::Type(QEvent::User + 2))); + + expected = + EventSpy::EventList() + << qMakePair(&object, QEvent::ChildAdded) + << qMakePair(&object, QEvent::ChildAdded) + << qMakePair(&object, QEvent::ChildRemoved); + QCOMPARE(spy.eventList(), expected); + spy.clear(); + + QCoreApplication::processEvents(); + + expected = + EventSpy::EventList() + << qMakePair(&object, QEvent::Type(QEvent::User + 1)) + << qMakePair(&object, QEvent::Type(QEvent::User + 2)); + QCOMPARE(spy.eventList(), expected); + } +} + +void tst_QObject::installEventFilter() +{ + QEvent event(QEvent::User); + EventSpy::EventList expected; + + QObject object; + EventSpy spy; + object.installEventFilter(&spy); + + QThread* otherthread = new QThread(); + QVERIFY(otherthread != object.thread()); + + // nothing special, should just work + 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(otherthread); + 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()); + + delete otherthread; +} + +class EmitThread : public QThread +{ + Q_OBJECT +public: + void run(void) { + emit work(); + } +signals: + void work(); +}; + +class DeleteObject : public QObject +{ + Q_OBJECT + +public slots: + void deleteSelf() + { + delete this; + } + + void relaySignalAndProcessEvents() + { + emit relayedSignal(); + QCoreApplication::processEvents(); + } + +signals: + void relayedSignal(); +}; + +void tst_QObject::deleteSelfInSlot() +{ + { + SenderObject sender; + DeleteObject *receiver = new 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 p = receiver; + sender.emitSignal1(); + QVERIFY(p.isNull()); + + QVERIFY(thread.wait(10000)); + } + + { + SenderObject sender; + DeleteObject *receiver = new 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 p = receiver; + sender.emitSignal1(); + QVERIFY(p.isNull()); + + QVERIFY(thread.wait(10000)); + } + + { + EmitThread sender; + DeleteObject *receiver = new DeleteObject(); + connect(&sender, SIGNAL(work()), receiver, SLOT(deleteSelf()), Qt::DirectConnection); + QPointer 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 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 p = receiver; + sender.emitSignal1(); + QVERIFY(!p.isNull()); + + receiver->deleteLater(); + + QVERIFY(thread.wait(10000)); + QVERIFY(p.isNull()); + } +} + +void tst_QObject::dumpObjectInfo() +{ + QObject a, b; + QObject::connect(&a, SIGNAL(destroyed(QObject *)), &b, SLOT(deleteLater())); + a.disconnect(&b); + a.dumpObjectInfo(); // should not crash +} + +class ConnectToSender : public QObject +{ + Q_OBJECT +public slots: + void uselessSlot() { count++; } + + void harmfullSlot() { + //this used to crash + connect(sender(), SIGNAL(signal4()), this, SLOT(uselessSlot())); + //play a little bit with the memory in order to really get a segfault. + connect(sender(), SIGNAL(signal1()), this, SLOT(uselessSlot())); + QList() << 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(ptr)); + QVERIFY(qobject_cast(cptr)); +} + +void tst_QObject::uniqConnection() +{ + SenderObject *s = new SenderObject; + ReceiverObject *r1 = new ReceiverObject; + ReceiverObject *r2 = new ReceiverObject; + r1->reset(); + r2->reset(); + ReceiverObject::sequence = 0; + + QVERIFY( connect( s, SIGNAL( signal1() ), r1, SLOT( slot1() ) , Qt::UniqueConnection) ); + QVERIFY( connect( s, SIGNAL( signal1() ), r2, SLOT( slot1() ) , Qt::UniqueConnection) ); + QVERIFY( connect( s, SIGNAL( signal1() ), r1, SLOT( slot3() ) , Qt::UniqueConnection) ); + QVERIFY( connect( s, SIGNAL( signal3() ), r1, SLOT( slot3() ) , Qt::UniqueConnection) ); + + s->emitSignal1(); + s->emitSignal2(); + s->emitSignal3(); + s->emitSignal4(); + + QCOMPARE( r1->count_slot1, 1 ); + QCOMPARE( r1->count_slot2, 0 ); + QCOMPARE( r1->count_slot3, 2 ); + QCOMPARE( r1->count_slot4, 0 ); + QCOMPARE( r2->count_slot1, 1 ); + QCOMPARE( r2->count_slot2, 0 ); + QCOMPARE( r2->count_slot3, 0 ); + QCOMPARE( r2->count_slot4, 0 ); + QCOMPARE( r1->sequence_slot1, 1 ); + QCOMPARE( r2->sequence_slot1, 2 ); + QCOMPARE( r1->sequence_slot3, 4 ); + + r1->reset(); + r2->reset(); + ReceiverObject::sequence = 0; + + QVERIFY( connect( s, SIGNAL( signal4() ), r1, SLOT( slot4() ) , Qt::UniqueConnection) ); + QVERIFY( connect( s, SIGNAL( signal4() ), r2, SLOT( slot4() ) , Qt::UniqueConnection) ); + QVERIFY(!connect( s, SIGNAL( signal4() ), r2, SLOT( slot4() ) , Qt::UniqueConnection) ); + QVERIFY( connect( s, SIGNAL( signal1() ), r2, SLOT( slot4() ) , Qt::UniqueConnection) ); + QVERIFY(!connect( s, SIGNAL( signal4() ), r1, SLOT( slot4() ) , Qt::UniqueConnection) ); + + s->emitSignal4(); + QCOMPARE( r1->count_slot4, 1 ); + QCOMPARE( r2->count_slot4, 1 ); + QCOMPARE( r1->sequence_slot4, 1 ); + QCOMPARE( r2->sequence_slot4, 2 ); + + r1->reset(); + r2->reset(); + ReceiverObject::sequence = 0; + + connect( s, SIGNAL( signal4() ), r1, SLOT( slot4() ) ); + + s->emitSignal4(); + QCOMPARE( r1->count_slot4, 2 ); + QCOMPARE( r2->count_slot4, 1 ); + QCOMPARE( r1->sequence_slot4, 3 ); + QCOMPARE( r2->sequence_slot4, 2 ); + + delete s; + delete r1; + delete r2; +} + +void tst_QObject::interfaceIid() +{ + QCOMPARE(QByteArray(qobject_interface_iid()), + QByteArray(Bleh_iid)); + QCOMPARE(QByteArray(qobject_interface_iid()), + QByteArray("com.qtest.foobar")); + QCOMPARE(QByteArray(qobject_interface_iid()), + QByteArray()); +} + +void tst_QObject::deleteQObjectWhenDeletingEvent() +{ + //this is related to task 259514 + //before the fix this used to dead lock when the QObject from the event was destroyed + + struct MyEvent : public QEvent + { + MyEvent() : QEvent(QEvent::User) { } + QObject obj; + }; + + QObject o; + QApplication::postEvent(&o, new MyEvent); + QCoreApplication::removePostedEvents(&o); // here you would get a deadlock +} + +class OverloadObject : public QObject +{ + friend class tst_QObject; + Q_OBJECT +signals: + void sig(int i, char c, qreal m = 12); + void sig(int i, int j = 12); + void sig(QObject *o, QObject *p, QObject *q = 0, QObject *r = 0) const; + void other(int a = 0); + void sig(QObject *o, OverloadObject *p = 0, QObject *q = 0, QObject *r = 0); + void sig(double r = 0.5); +public slots: + void slo(int i, int j = 43) + { + s_num += 1; + i1_num = i; + i2_num = j; + } + void slo(QObject *o, QObject *p = qApp, QObject *q = qApp, QObject *r = qApp) + { + s_num += 10; + o1_obj = o; + o2_obj = p; + o3_obj = q; + o4_obj = r; + } + void slo() + { + s_num += 100; + } + +public: + int s_num; + int i1_num; + int i2_num; + QObject *o1_obj; + QObject *o2_obj; + QObject *o3_obj; + QObject *o4_obj; +}; + +void tst_QObject::overloads() +{ + OverloadObject obj1; + OverloadObject obj2; + QObject obj3; + obj1.s_num = 0; + obj2.s_num = 0; + + connect (&obj1, SIGNAL(sig(int)) , &obj1, SLOT(slo(int))); + connect (&obj1, SIGNAL(sig(QObject *, QObject *, QObject *)) , &obj1, SLOT(slo(QObject * , QObject *, QObject *))); + + connect (&obj1, SIGNAL(sig(QObject *, QObject *, QObject *, QObject *)) , &obj2, SLOT(slo(QObject * , QObject *, QObject *))); + connect (&obj1, SIGNAL(sig(QObject *)) , &obj2, SLOT(slo())); + connect (&obj1, SIGNAL(sig(int, int)) , &obj2, SLOT(slo(int, int))); + + emit obj1.sig(0.5); //connected to nothing + emit obj1.sig(1, 'a'); //connected to nothing + QCOMPARE(obj1.s_num, 0); + QCOMPARE(obj2.s_num, 0); + + emit obj1.sig(1); //this signal is connected + QCOMPARE(obj1.s_num, 1); + QCOMPARE(obj1.i1_num, 1); + QCOMPARE(obj1.i2_num, 43); //default argument of the slot + + QCOMPARE(obj2.s_num, 1); + QCOMPARE(obj2.i1_num, 1); + QCOMPARE(obj2.i2_num, 12); //default argument of the signal + + + emit obj1.sig(&obj2); //this signal is conencted to obj2 + QCOMPARE(obj1.s_num, 1); + QCOMPARE(obj2.s_num, 101); + emit obj1.sig(&obj2, &obj3); //this signal is connected + QCOMPARE(obj1.s_num, 11); + QCOMPARE(obj1.o1_obj, (QObject *)&obj2); + QCOMPARE(obj1.o2_obj, &obj3); + QCOMPARE(obj1.o3_obj, (QObject *)0); //default arg of the signal + QCOMPARE(obj1.o4_obj, (QObject *)qApp); //default arg of the slot + + QCOMPARE(obj2.s_num, 111); + QCOMPARE(obj2.o1_obj, (QObject *)&obj2); + QCOMPARE(obj2.o2_obj, &obj3); + QCOMPARE(obj2.o3_obj, (QObject *)0); //default arg of the signal + QCOMPARE(obj2.o4_obj, (QObject *)qApp); //default arg of the slot +} + +class ManySignals : public QObject +{ Q_OBJECT + friend class tst_QObject; +signals: + void sig00(); void sig01(); void sig02(); void sig03(); void sig04(); + void sig05(); void sig06(); void sig07(); void sig08(); void sig09(); + void sig10(); void sig11(); void sig12(); void sig13(); void sig14(); + void sig15(); void sig16(); void sig17(); void sig18(); void sig19(); + void sig20(); void sig21(); void sig22(); void sig23(); void sig24(); + void sig25(); void sig26(); void sig27(); void sig28(); void sig29(); + void sig30(); void sig31(); void sig32(); void sig33(); void sig34(); + void sig35(); void sig36(); void sig37(); void sig38(); void sig39(); + void sig40(); void sig41(); void sig42(); void sig43(); void sig44(); + void sig45(); void sig46(); void sig47(); void sig48(); void sig49(); + void sig50(); void sig51(); void sig52(); void sig53(); void sig54(); + void sig55(); void sig56(); void sig57(); void sig58(); void sig59(); + void sig60(); void sig61(); void sig62(); void sig63(); void sig64(); + void sig65(); void sig66(); void sig67(); void sig68(); void sig69(); + +public slots: + void received() { rec++; } +public: + int rec; +}; + + +void tst_QObject::isSignalConnected() +{ + ManySignals o; + o.rec = 0; + QObjectPrivate *priv = QObjectPrivate::get(&o); + QVERIFY(!priv->isSignalConnected(priv->signalIndex("destroyed()"))); + QVERIFY(!priv->isSignalConnected(priv->signalIndex("sig00()"))); + QVERIFY(!priv->isSignalConnected(priv->signalIndex("sig05()"))); + QVERIFY(!priv->isSignalConnected(priv->signalIndex("sig15()"))); + QVERIFY(!priv->isSignalConnected(priv->signalIndex("sig29()"))); + QVERIFY(!priv->isSignalConnected(priv->signalIndex("sig60()"))); + QVERIFY(!priv->isSignalConnected(priv->signalIndex("sig61()"))); + + 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())); + + 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()"))); + + 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())); + + 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()"))); + + QCOMPARE(o.rec, 0); + emit o.sig01(); + emit o.sig34(); + QCOMPARE(o.rec, 0); + + QObject::connect(&o, SIGNAL(sig27()), &o, SLOT(received())); + + QVERIFY(priv->isSignalConnected(priv->signalIndex("sig00()"))); + QVERIFY(priv->isSignalConnected(priv->signalIndex("sig03()"))); + QVERIFY(priv->isSignalConnected(priv->signalIndex("sig34()"))); + QVERIFY(priv->isSignalConnected(priv->signalIndex("sig18()"))); + QVERIFY(priv->isSignalConnected(priv->signalIndex("sig62()"))); + QVERIFY(priv->isSignalConnected(priv->signalIndex("sig28()"))); + QVERIFY(priv->isSignalConnected(priv->signalIndex("sig69()"))); + QVERIFY(priv->isSignalConnected(priv->signalIndex("sig27()"))); + + QVERIFY(!priv->isSignalConnected(priv->signalIndex("sig04()"))); + QVERIFY(!priv->isSignalConnected(priv->signalIndex("sig21()"))); + QVERIFY(!priv->isSignalConnected(priv->signalIndex("sig25()"))); + QVERIFY(!priv->isSignalConnected(priv->signalIndex("sig55()"))); + QVERIFY(!priv->isSignalConnected(priv->signalIndex("sig61()"))); + + emit o.sig00(); + QCOMPARE(o.rec, 1); + emit o.sig69(); + QCOMPARE(o.rec, 2); + emit o.sig36(); + QCOMPARE(o.rec, 2); +} + +void tst_QObject::qMetaObjectConnect() +{ + SenderObject *s = new SenderObject; + ReceiverObject *r1 = new ReceiverObject; + ReceiverObject *r2 = new ReceiverObject; + r1->reset(); + r2->reset(); + ReceiverObject::sequence = 0; + + int signal1Index = s->metaObject()->indexOfSignal("signal1()"); + int signal3Index = s->metaObject()->indexOfSignal("signal3()"); + int slot1Index = r1->metaObject()->indexOfSlot("slot1()"); + int slot2Index = r1->metaObject()->indexOfSlot("slot2()"); + int slot3Index = r1->metaObject()->indexOfSlot("slot3()"); + + QVERIFY(slot1Index > 0); + QVERIFY(slot2Index > 0); + QVERIFY(slot3Index > 0); + + QVERIFY( QMetaObject::connect( s, signal1Index, r1, slot1Index) ); + QVERIFY( QMetaObject::connect( s, signal3Index, r2, slot3Index) ); + QVERIFY( QMetaObject::connect( s, -1, r2, slot2Index) ); + + QCOMPARE( r1->count_slot1, 0 ); + QCOMPARE( r1->count_slot2, 0 ); + QCOMPARE( r1->count_slot3, 0 ); + QCOMPARE( r2->count_slot1, 0 ); + QCOMPARE( r2->count_slot2, 0 ); + QCOMPARE( r2->count_slot3, 0 ); + + s->emitSignal1(); + + QCOMPARE( r1->count_slot1, 1 ); + QCOMPARE( r1->count_slot2, 0 ); + QCOMPARE( r1->count_slot3, 0 ); + QCOMPARE( r2->count_slot1, 0 ); + QCOMPARE( r2->count_slot2, 1 ); + QCOMPARE( r2->count_slot3, 0 ); + + s->emitSignal2(); + s->emitSignal3(); + s->emitSignal4(); + + QCOMPARE( r1->count_slot1, 1 ); + QCOMPARE( r1->count_slot2, 0 ); + QCOMPARE( r1->count_slot3, 0 ); + QCOMPARE( r2->count_slot1, 0 ); + QCOMPARE( r2->count_slot2, 4 ); + QCOMPARE( r2->count_slot3, 1 ); + + QVERIFY( QMetaObject::disconnect( s, signal1Index, r1, slot1Index) ); + QVERIFY( QMetaObject::disconnect( s, signal3Index, r2, slot3Index) ); + QVERIFY( QMetaObject::disconnect( s, -1, r2, slot2Index) ); + + s->emitSignal1(); + s->emitSignal2(); + s->emitSignal3(); + s->emitSignal4(); + + QCOMPARE( r1->count_slot1, 1 ); + QCOMPARE( r1->count_slot2, 0 ); + QCOMPARE( r1->count_slot3, 0 ); + QCOMPARE( r2->count_slot1, 0 ); + QCOMPARE( r2->count_slot2, 4 ); + QCOMPARE( r2->count_slot3, 1 ); + + //some "dynamic" signal + QVERIFY( QMetaObject::connect( s, s->metaObject()->methodOffset() + 20, r1, slot3Index) ); + QVERIFY( QMetaObject::connect( s, s->metaObject()->methodOffset() + 35, r2, slot1Index) ); + QVERIFY( QMetaObject::connect( s, -1, r1, slot2Index) ); + + r1->reset(); + r2->reset(); + + void *args[] = { 0 , 0 }; + QMetaObject::activate(s, s->metaObject()->methodOffset() + 20, args); + QMetaObject::activate(s, s->metaObject()->methodOffset() + 48, args); + QCOMPARE( r1->count_slot1, 0 ); + QCOMPARE( r1->count_slot2, 2 ); + QCOMPARE( r1->count_slot3, 1 ); + QCOMPARE( r2->count_slot1, 0 ); + QCOMPARE( r2->count_slot2, 0 ); + QCOMPARE( r2->count_slot3, 0 ); + + QMetaObject::activate(s, s->metaObject()->methodOffset() + 35, args); + s->emitSignal1(); + s->emitSignal2(); + + QCOMPARE( r1->count_slot1, 0 ); + QCOMPARE( r1->count_slot2, 5 ); + QCOMPARE( r1->count_slot3, 1 ); + QCOMPARE( r2->count_slot1, 1 ); + QCOMPARE( r2->count_slot2, 0 ); + QCOMPARE( r2->count_slot3, 0 ); + + delete s; + r1->reset(); + r2->reset(); + +#define SIGNAL_INDEX(S) obj1.metaObject()->indexOfSignal(QMetaObject::normalizedSignature(#S)) + OverloadObject obj1; + QObject obj2, obj3; + + QMetaObject::connect(&obj1, SIGNAL_INDEX(sig(int)) , r1, slot1Index); + QMetaObject::connect(&obj1, SIGNAL_INDEX(sig(QObject *, QObject *, QObject *)) , r2, slot1Index); + + QMetaObject::connect(&obj1, SIGNAL_INDEX(sig(QObject *, QObject *, QObject *, QObject *)) , r1, slot2Index); + QMetaObject::connect(&obj1, SIGNAL_INDEX(sig(QObject *)) , r2, slot2Index); + QMetaObject::connect(&obj1, SIGNAL_INDEX(sig(int, int)) , r1, slot3Index); + + emit obj1.sig(0.5); //connected to nothing + emit obj1.sig(1, 'a'); //connected to nothing + QCOMPARE( r1->count_slot1, 0 ); + QCOMPARE( r1->count_slot2, 0 ); + QCOMPARE( r1->count_slot3, 0 ); + QCOMPARE( r2->count_slot1, 0 ); + QCOMPARE( r2->count_slot2, 0 ); + QCOMPARE( r2->count_slot3, 0 ); + + emit obj1.sig(1); //this signal is connected + emit obj1.sig(&obj2); + + QCOMPARE( r1->count_slot1, 1 ); + QCOMPARE( r1->count_slot2, 0 ); + QCOMPARE( r1->count_slot3, 1 ); + QCOMPARE( r2->count_slot1, 0 ); + QCOMPARE( r2->count_slot2, 1 ); + QCOMPARE( r2->count_slot3, 0 ); + + emit obj1.sig(&obj2, &obj3); //this signal is connected + + QCOMPARE( r1->count_slot1, 1 ); + QCOMPARE( r1->count_slot2, 1 ); + QCOMPARE( r1->count_slot3, 1 ); + QCOMPARE( r2->count_slot1, 1 ); + QCOMPARE( r2->count_slot2, 1 ); + QCOMPARE( r2->count_slot3, 0 ); + + delete r1; + delete r2; + +} + +void tst_QObject::qMetaObjectDisconnectOne() +{ + SenderObject *s = new SenderObject; + ReceiverObject *r1 = new ReceiverObject; + + int signal1Index = s->metaObject()->indexOfSignal("signal1()"); + int signal3Index = s->metaObject()->indexOfSignal("signal3()"); + int slot1Index = r1->metaObject()->indexOfSlot("slot1()"); + int slot2Index = r1->metaObject()->indexOfSlot("slot2()"); + + QVERIFY(signal1Index > 0); + QVERIFY(signal3Index > 0); + QVERIFY(slot1Index > 0); + QVERIFY(slot2Index > 0); + + QVERIFY( QMetaObject::connect(s, signal1Index, r1, slot1Index) ); + QVERIFY( QMetaObject::connect(s, signal3Index, r1, slot2Index) ); + QVERIFY( QMetaObject::connect(s, signal3Index, r1, slot2Index) ); + QVERIFY( QMetaObject::connect(s, signal3Index, r1, slot2Index) ); + + r1->reset(); + QCOMPARE( r1->count_slot1, 0 ); + QCOMPARE( r1->count_slot2, 0 ); + + s->emitSignal1(); + QCOMPARE( r1->count_slot1, 1 ); + QCOMPARE( r1->count_slot2, 0 ); + + s->emitSignal3(); + QCOMPARE( r1->count_slot1, 1 ); + QCOMPARE( r1->count_slot2, 3 ); + + r1->reset(); + QVERIFY( QMetaObject::disconnectOne(s, signal1Index, r1, slot1Index) ); + QVERIFY( QMetaObject::disconnectOne(s, signal3Index, r1, slot2Index) ); + + s->emitSignal1(); + QCOMPARE( r1->count_slot1, 0 ); + QCOMPARE( r1->count_slot2, 0 ); + + s->emitSignal3(); + QCOMPARE( r1->count_slot1, 0 ); + QCOMPARE( r1->count_slot2, 2 ); + + r1->reset(); + QVERIFY( false == QMetaObject::disconnectOne(s, signal1Index, r1, slot1Index) ); + QVERIFY( QMetaObject::disconnectOne(s, signal3Index, r1, slot2Index) ); + + s->emitSignal1(); + QCOMPARE( r1->count_slot1, 0 ); + QCOMPARE( r1->count_slot2, 0 ); + + s->emitSignal3(); + QCOMPARE( r1->count_slot1, 0 ); + QCOMPARE( r1->count_slot2, 1 ); + + r1->reset(); + QVERIFY( false == QMetaObject::disconnectOne(s, signal1Index, r1, slot1Index) ); + QVERIFY( QMetaObject::disconnectOne(s, signal3Index, r1, slot2Index) ); + + s->emitSignal1(); + QCOMPARE( r1->count_slot1, 0 ); + QCOMPARE( r1->count_slot2, 0 ); + + s->emitSignal3(); + QCOMPARE( r1->count_slot1, 0 ); + QCOMPARE( r1->count_slot2, 0 ); + + delete s; + delete r1; +} + +class ConfusingObject : public SenderObject +{ + Q_OBJECT +public slots: + void signal1() { s++; } +signals: + void aPublicSlot(); +public: + int s; + ConfusingObject() : s(0) {} + friend class tst_QObject; +}; + +void tst_QObject::sameName() +{ + ConfusingObject c1, c2; + QVERIFY(connect(&c1, SIGNAL(signal1()), &c1, SLOT(signal1()))); + c1.emitSignal1(); + QCOMPARE(c1.s, 1); + + QVERIFY(connect(&c2, SIGNAL(signal1()), &c1, SIGNAL(signal1()))); + c2.emitSignal1(); + QCOMPARE(c1.s, 2); + + QVERIFY(connect(&c2, SIGNAL(aPublicSlot()), &c1, SLOT(signal1()))); + c2.aPublicSlot(); + QCOMPARE(c2.aPublicSlotCalled, 0); + QCOMPARE(c1.aPublicSlotCalled, 0); + QCOMPARE(c1.s, 3); + + QVERIFY(connect(&c2, SIGNAL(aPublicSlot()), &c1, SLOT(aPublicSlot()))); + c2.aPublicSlot(); + QCOMPARE(c2.aPublicSlotCalled, 0); + QCOMPARE(c1.aPublicSlotCalled, 1); + QCOMPARE(c1.s, 4); +} + +void tst_QObject::connectByMetaMethods() +{ + SenderObject s; + ReceiverObject r; + const QMetaObject *smeta = s.metaObject(); + const QMetaObject *rmeta = r.metaObject(); + int sigIndx = smeta->indexOfSignal(QMetaObject::normalizedSignature("signal1()")); + int slotIndx = rmeta->indexOfSlot(QMetaObject::normalizedSignature("slot1()")); + QVERIFY( sigIndx != -1 ); + QVERIFY( slotIndx != -1 ); + QMetaMethod signal = smeta->method(sigIndx); + QMetaMethod slot = rmeta->method(slotIndx); + + QVERIFY(connect(&s,signal, &r,slot)); + + QVERIFY(!r.called(1)); + s.emitSignal1(); + QVERIFY(r.called(1)); +} + +void tst_QObject::connectByMetaMethodSlotInsteadOfSignal() +{ + SenderObject s; + ReceiverObject r; + const QMetaObject *smeta = s.metaObject(); + const QMetaObject *rmeta = r.metaObject(); + int badIndx = smeta->indexOfSlot(QMetaObject::normalizedSignature("aPublicSlot()")); + int slotIndx = rmeta->indexOfSlot(QMetaObject::normalizedSignature("slot1()")); + QVERIFY( badIndx != -1 ); + QVERIFY( slotIndx != -1 ); + QMetaMethod badMethod = smeta->method(badIndx); + QMetaMethod slot = rmeta->method(slotIndx); + + QTest::ignoreMessage(QtWarningMsg,"QObject::connect: Cannot connect SenderObject::aPublicSlot() to ReceiverObject::slot1()"); + QVERIFY(!connect(&s,badMethod, &r,slot)); +} + +class Constructable: public QObject +{ + Q_OBJECT + +public: + Q_INVOKABLE Constructable(){} + +}; + +void tst_QObject::connectConstructorByMetaMethod() +{ + Constructable sc; + Constructable rc; + SenderObject s; + ReceiverObject r; + + const QMetaObject cmeta = Constructable::staticMetaObject; + const QMetaObject *smeta = s.metaObject(); + const QMetaObject *rmeta = r.metaObject(); + int constructorIndx = cmeta.indexOfConstructor(QMetaObject::normalizedSignature("Constructable()")); + int sigIndx = smeta->indexOfSignal(QMetaObject::normalizedSignature("signal1()")); + int slotIndx = rmeta->indexOfSlot(QMetaObject::normalizedSignature("slot1()")); + QVERIFY( constructorIndx != -1 ); + QVERIFY( sigIndx != -1 ); + QVERIFY( slotIndx != -1 ); + + QMetaMethod constructor = cmeta.constructor(constructorIndx); + QMetaMethod signal = smeta->method(sigIndx); + QMetaMethod slot = rmeta->method(slotIndx); + + QTest::ignoreMessage(QtWarningMsg,"QObject::connect: Cannot connect Constructable::Constructable() to ReceiverObject::slot1()"); + QVERIFY(!connect(&sc,constructor, &r,slot)); + QTest::ignoreMessage(QtWarningMsg,"QObject::connect: Cannot connect SenderObject::signal1() to Constructable::Constructable()"); + QVERIFY(!connect(&s,signal, &rc,constructor)); + QTest::ignoreMessage(QtWarningMsg,"QObject::connect: Cannot connect Constructable::Constructable() to Constructable::Constructable()"); + QVERIFY(!connect(&sc,constructor, &rc,constructor)); +} + +void tst_QObject::disconnectByMetaMethod() +{ + SenderObject *s = new SenderObject; + ReceiverObject *r1 = new ReceiverObject; + ReceiverObject *r2 = new ReceiverObject; + + QMetaMethod signal1 = s->metaObject()->method( + s->metaObject()->indexOfMethod("signal1()")); + QMetaMethod signal2 = s->metaObject()->method( + s->metaObject()->indexOfMethod("signal2()")); + QMetaMethod signal3 = s->metaObject()->method( + s->metaObject()->indexOfMethod("signal3()")); + QMetaMethod signal4 = s->metaObject()->method( + s->metaObject()->indexOfMethod("signal4()")); + + QMetaMethod slot1 = r1->metaObject()->method( + r1->metaObject()->indexOfMethod("slot1()")); + QMetaMethod slot2 = r1->metaObject()->method( + r1->metaObject()->indexOfMethod("slot2()")); + QMetaMethod slot3 = r1->metaObject()->method( + r1->metaObject()->indexOfMethod("slot3()")); + QMetaMethod slot4 = r1->metaObject()->method( + r1->metaObject()->indexOfMethod("slot4()")); + + connect(s, signal1, r1, slot1); + + s->emitSignal1(); + + QVERIFY(r1->called(1)); + r1->reset(); + + // usual disconnect with all parameters given + bool ret = QObject::disconnect(s, signal1, r1, slot1); + + s->emitSignal1(); + + QVERIFY(!r1->called(1)); + r1->reset(); + + QVERIFY(ret); + ret = QObject::disconnect(s, signal1, r1, slot1); + QVERIFY(!ret); + + r1->reset(); + + connect( s, signal1, r1, slot1 ); + connect( s, signal1, r1, slot2 ); + connect( s, signal1, r1, slot3 ); + connect( s, signal2, r1, slot4 ); + + // disconnect s's signal1() from all slots of r1 + QObject::disconnect(s, signal1, r1, QMetaMethod()); + + s->emitSignal1(); + s->emitSignal2(); + + QVERIFY(!r1->called(1)); + QVERIFY(!r1->called(2)); + QVERIFY(!r1->called(3)); + QVERIFY(r1->called(4)); + r1->reset(); + // make sure all is disconnected again + QObject::disconnect(s, 0, r1, 0); + + connect(s, signal1, r1, slot1); + connect(s, signal1, r2, slot1); + connect(s, signal2, r1, slot2); + connect(s, signal2, r2, slot2); + connect(s, signal3, r1, slot3); + connect(s, signal3, r2, slot3); + + // disconnect signal1() from all receivers + QObject::disconnect(s, signal1, 0, QMetaMethod()); + s->emitSignal1(); + s->emitSignal2(); + s->emitSignal3(); + + QVERIFY(!r1->called(1)); + QVERIFY(!r2->called(1)); + QVERIFY(r1->called(2)); + QVERIFY(r2->called(2)); + QVERIFY(r1->called(2)); + QVERIFY(r2->called(2)); + + r1->reset(); + r2->reset(); + + // disconnect all signals of s from all receivers + QObject::disconnect( s, 0, 0, 0 ); + + connect( s, signal1, r1, slot1 ); + connect( s, signal1, r2, slot1 ); + + // disconnect all signals from slot1 of r1 + QObject::disconnect(s, QMetaMethod(), r1, slot1); + + s->emitSignal1(); + + QVERIFY(!r1->called(1)); + QVERIFY(r2->called(1)); + + delete r2; + delete r1; + delete s; +} + +void tst_QObject::disconnectNotSignalMetaMethod() +{ + SenderObject s; + ReceiverObject r; + + connect(&s, SIGNAL(signal1()), &r, SLOT(slot1())); + + QMetaMethod slot = s.metaObject()->method( + s.metaObject()->indexOfMethod("aPublicSlot()")); + + QTest::ignoreMessage(QtWarningMsg,"Object::disconnect: Attempt to unbind non-signal SenderObject::aPublicSlot()"); + QVERIFY(!QObject::disconnect(&s, slot, &r, QMetaMethod())); +} + +class ThreadAffinityThread : public QThread +{ +public: + SenderObject *sender; + + ThreadAffinityThread(SenderObject *sender) + : sender(sender) + { } + void run() + { + sender->emitSignal1(); + } +}; + +void tst_QObject::autoConnectionBehavior() +{ + SenderObject *sender = new SenderObject; + ReceiverObject *receiver = new ReceiverObject; + connect(sender, SIGNAL(signal1()), receiver, SLOT(slot1())); + + // at emit, currentThread == sender->thread(), currentThread == receiver->thread(), sender->thread() == receiver->thread() + QVERIFY(!receiver->called(1)); + sender->emitSignal1(); + QVERIFY(receiver->called(1)); + receiver->reset(); + + // at emit, currentThread != sender->thread(), currentThread != receiver->thread(), sender->thread() == receiver->thread() + ThreadAffinityThread emitThread1(sender); + QVERIFY(!receiver->called(1)); + emitThread1.start(); + QVERIFY(emitThread1.wait(30000)); + QVERIFY(!receiver->called(1)); + QCoreApplication::sendPostedEvents(receiver, QEvent::MetaCall); + QVERIFY(receiver->called(1)); + receiver->reset(); + + // at emit, currentThread == sender->thread(), currentThread != receiver->thread(), sender->thread() != receiver->thread() + sender->moveToThread(&emitThread1); + QVERIFY(!receiver->called(1)); + emitThread1.start(); + QVERIFY(emitThread1.wait(30000)); + QVERIFY(!receiver->called(1)); + QCoreApplication::sendPostedEvents(receiver, QEvent::MetaCall); + QVERIFY(receiver->called(1)); + receiver->reset(); + + // at emit, currentThread != sender->thread(), currentThread == receiver->thread(), sender->thread() != receiver->thread() + QVERIFY(!receiver->called(1)); + sender->emitSignal1(); + QVERIFY(receiver->called(1)); + receiver->reset(); + + // at emit, currentThread != sender->thread(), currentThread != receiver->thread(), sender->thread() != receiver->thread() + ThreadAffinityThread emitThread2(sender); + QThread receiverThread; + QTimer *timer = new QTimer; + timer->setSingleShot(true); + timer->setInterval(100); + connect(&receiverThread, SIGNAL(started()), timer, SLOT(start())); + connect(timer, SIGNAL(timeout()), &receiverThread, SLOT(quit()), Qt::DirectConnection); + connect(&receiverThread, SIGNAL(finished()), timer, SLOT(deleteLater())); + timer->moveToThread(&receiverThread); + + receiver->moveToThread(&receiverThread); + QVERIFY(!receiver->called(1)); + emitThread2.start(); + QVERIFY(emitThread2.wait(30000)); + QVERIFY(!receiver->called(1)); + receiverThread.start(); + QVERIFY(receiverThread.wait(30000)); + QVERIFY(receiver->called(1)); + receiver->reset(); + + delete sender; + delete receiver; +} + +class BaseDestroyed : public QObject +{ + Q_OBJECT + QList fooList; + bool destroyed; +public: + BaseDestroyed() : destroyed(false) + { fooList << "a" << "b"; } + ~BaseDestroyed() + { + QVERIFY(!destroyed); + destroyed = true; + } + +public slots: + void slotUseList() + { + QVERIFY(!destroyed); + fooList << "c" << "d"; + } +}; + +void tst_QObject::baseDestroyed() +{ + BaseDestroyed d; + connect(&d, SIGNAL(destroyed()), &d, SLOT(slotUseList())); + //When d goes out of scope, slotUseList should not be called as the BaseDestroyed has + // already been destroyed while ~QObject emit destroyed +} + +QTEST_MAIN(tst_QObject) + +#include "moc_tst_qobject.cpp"