summaryrefslogtreecommitdiffstats
path: root/tests/auto/corelib/kernel
diff options
context:
space:
mode:
authorDario Freddi <dario.freddi@ispirata.com>2013-07-17 00:39:23 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-09-16 23:46:30 +0200
commit76b06f993e534e02e35830ac0d8f41e546463945 (patch)
tree08a76949780dc0e74214c3052efbedaa741fc4fb /tests/auto/corelib/kernel
parentae577e6b8f8b966cf87293b12803bcc813019a2e (diff)
QObject: allow connecting to functors with a receiver object
Up to now, it was only possible to connect to functors in a direct way, without being capable of using Qt::ConnectionType. This patch allows for specifying a receiver for Functors and function pointers, hence making it possible to specify effectively the connection type. To do this properly, it was needed to add an enum in FunctionPointer representing whether the considered function is a member function or not, to reduce ambiguity upon overloaded calls. Moreover, now senders are checked for the existence of a slot obj as well. This way, should the context be freed, the slot obj and the functor contained in it will be freed as well. On a side note, connecting to a static slot (like QCoreApplication::quit) specifying the receiver object is now compiling. Change-Id: I46474099413b1dc6ca4db9934191d469baeef070 Reviewed-by: Olivier Goffart <ogoffart@woboq.com>
Diffstat (limited to 'tests/auto/corelib/kernel')
-rw-r--r--tests/auto/corelib/kernel/qobject/tst_qobject.cpp209
1 files changed, 207 insertions, 2 deletions
diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp
index 081dc8a857..05d81c2bd1 100644
--- a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp
+++ b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp
@@ -90,7 +90,7 @@ private slots:
void thread();
void thread0();
void moveToThread();
- void sender();
+ void senderTest();
void declareInterface();
void qpointerResetBeforeDestroyedSignal();
void testUserData();
@@ -144,7 +144,11 @@ private slots:
void connectPrivateSlots();
void connectFunctorArgDifference();
void connectFunctorOverloads();
+ void connectFunctorQueued();
+ void connectFunctorWithContext();
+ void connectStaticSlotWithObject();
void disconnectDoesNotLeakFunctor();
+ void contextDoesNotLeakFunctor();
void connectBase();
void qmlConnect();
};
@@ -2152,7 +2156,7 @@ signals:
void theSignal();
};
-void tst_QObject::sender()
+void tst_QObject::senderTest()
{
{
SuperObject sender;
@@ -5602,6 +5606,129 @@ void tst_QObject::connectFunctorArgDifference()
QVERIFY(true);
}
+class ContextObject : public QObject
+{
+ Q_OBJECT
+public:
+ void compareSender(QObject *s) { QCOMPARE(s, sender()); }
+};
+
+struct SlotArgFunctor
+{
+ SlotArgFunctor(int *s) : status(s), context(Q_NULLPTR), sender(Q_NULLPTR) {}
+ SlotArgFunctor(ContextObject *context, QObject *sender, int *s) : status(s), context(context), sender(sender) {}
+ void operator()() { *status = 2; if (context) context->compareSender(sender); }
+
+protected:
+ int *status;
+ ContextObject *context;
+ QObject *sender;
+};
+
+void tst_QObject::connectFunctorQueued()
+{
+ int status = 1;
+ SenderObject obj;
+ QEventLoop e;
+
+ connect(&obj, &SenderObject::signal1, this, SlotArgFunctor(&status), Qt::QueuedConnection);
+ connect(&obj, &SenderObject::signal1, &e, &QEventLoop::quit, Qt::QueuedConnection);
+
+ obj.emitSignal1();
+ QCOMPARE(status, 1);
+ e.exec();
+ QCOMPARE(status, 2);
+
+#if defined(Q_COMPILER_LAMBDA)
+ status = 1;
+ connect(&obj, &SenderObject::signal1, this, [&status] { status = 2; }, Qt::QueuedConnection);
+
+ obj.emitSignal1();
+ QCOMPARE(status, 1);
+ e.exec();
+ QCOMPARE(status, 2);
+#endif
+}
+
+void tst_QObject::connectFunctorWithContext()
+{
+ int status = 1;
+ SenderObject obj;
+ QMetaObject::Connection handle;
+ ContextObject *context = new ContextObject;
+ QEventLoop e;
+
+ connect(&obj, &SenderObject::signal1, context, SlotArgFunctor(&status));
+ connect(&obj, &SenderObject::signal1, &e, &QEventLoop::quit, Qt::QueuedConnection);
+
+ // When the context gets deleted, the connection should decay and the signal shouldn't trigger
+ // The connection is queued to make sure the destroyed signal propagates correctly and
+ // cuts the connection.
+ connect(context, &QObject::destroyed, &obj, &SenderObject::signal1, Qt::QueuedConnection);
+ context->deleteLater();
+
+ QCOMPARE(status, 1);
+ e.exec();
+ QCOMPARE(status, 1);
+
+ // Check the sender arg is set correctly in the context
+ context = new ContextObject;
+
+ connect(&obj, &SenderObject::signal1, context,
+ SlotArgFunctor(context, &obj, &status), Qt::QueuedConnection);
+
+ obj.emitSignal1();
+ QCOMPARE(status, 1);
+ e.exec();
+ QCOMPARE(status, 2);
+
+#if defined(Q_COMPILER_LAMBDA)
+ status = 1;
+ connect(&obj, &SenderObject::signal1, this, [this, &status, &obj] { status = 2; QCOMPARE(sender(), &obj); }, Qt::QueuedConnection);
+
+ obj.emitSignal1();
+ QCOMPARE(status, 1);
+ e.exec();
+ QCOMPARE(status, 2);
+#endif
+
+ // Free
+ context->deleteLater();
+}
+
+static int s_static_slot_checker = 1;
+
+class StaticSlotChecker : public QObject
+{
+ Q_OBJECT
+public Q_SLOTS:
+ static void staticSlot() { s_static_slot_checker = 2; }
+};
+
+void tst_QObject::connectStaticSlotWithObject()
+{
+ SenderObject sender;
+ StaticSlotChecker *receiver = new StaticSlotChecker;
+ QEventLoop e;
+
+ QVERIFY(connect(&sender, &SenderObject::signal1, receiver, &StaticSlotChecker::staticSlot, Qt::QueuedConnection));
+ connect(&sender, &SenderObject::signal1, &e, &QEventLoop::quit, Qt::QueuedConnection);
+
+ sender.emitSignal1();
+ QCOMPARE(s_static_slot_checker, 1);
+ e.exec();
+ QCOMPARE(s_static_slot_checker, 2);
+
+ s_static_slot_checker = 1;
+
+ connect(receiver, &QObject::destroyed, &sender, &SenderObject::signal1, Qt::QueuedConnection);
+ receiver->deleteLater();
+
+ QCOMPARE(s_static_slot_checker, 1);
+ e.exec();
+ QCOMPARE(s_static_slot_checker, 1);
+}
+
struct ComplexFunctor {
ComplexFunctor(int &overload, QList<QVariant> &result) : overload(overload), result(result) {}
void operator()(int a, int b) {
@@ -5774,6 +5901,23 @@ void tst_QObject::disconnectDoesNotLeakFunctor()
}
QCOMPARE(countedStructObjectsCount, 0);
{
+ GetSenderObject obj;
+ QMetaObject::Connection c;
+ {
+ CountedStruct s(&obj);
+ QObject context;
+ QCOMPARE(countedStructObjectsCount, 1);
+
+ c = connect(&obj, &GetSenderObject::aSignal, &context, s);
+ QVERIFY(c);
+ QCOMPARE(countedStructObjectsCount, 2);
+ QVERIFY(QObject::disconnect(c));
+ QCOMPARE(countedStructObjectsCount, 1);
+ }
+ QCOMPARE(countedStructObjectsCount, 0);
+ }
+ QCOMPARE(countedStructObjectsCount, 0);
+ {
QMetaObject::Connection c1, c2;
{
CountedStruct s;
@@ -5857,6 +6001,67 @@ void tst_QObject::disconnectDoesNotLeakFunctor()
QCOMPARE(countedStructObjectsCount, 0);
}
+void tst_QObject::contextDoesNotLeakFunctor()
+{
+ QCOMPARE(countedStructObjectsCount, 0);
+ {
+ QMetaObject::Connection c;
+ {
+ QEventLoop e;
+ ContextObject *context = new ContextObject;
+ SenderObject obj;
+
+ connect(&obj, &SenderObject::signal1, context, CountedStruct());
+ connect(context, &QObject::destroyed, &e, &QEventLoop::quit, Qt::QueuedConnection);
+ context->deleteLater();
+
+ QCOMPARE(countedStructObjectsCount, 1);
+ e.exec();
+ QCOMPARE(countedStructObjectsCount, 0);
+ }
+ QCOMPARE(countedStructObjectsCount, 0);
+ }
+ QCOMPARE(countedStructObjectsCount, 0);
+ {
+ GetSenderObject obj;
+ QMetaObject::Connection c;
+ {
+ CountedStruct s(&obj);
+ QEventLoop e;
+ ContextObject *context = new ContextObject;
+ QCOMPARE(countedStructObjectsCount, 1);
+
+ connect(&obj, &GetSenderObject::aSignal, context, s);
+ QCOMPARE(countedStructObjectsCount, 2);
+
+ connect(context, &QObject::destroyed, &e, &QEventLoop::quit, Qt::QueuedConnection);
+ context->deleteLater();
+
+ e.exec();
+ QCOMPARE(countedStructObjectsCount, 1);
+ }
+ QCOMPARE(countedStructObjectsCount, 0);
+ }
+ QCOMPARE(countedStructObjectsCount, 0);
+ {
+#if defined(Q_COMPILER_LAMBDA)
+ CountedStruct s;
+ QEventLoop e;
+ ContextObject *context = new ContextObject;
+ QCOMPARE(countedStructObjectsCount, 1);
+ QTimer timer;
+
+ connect(&timer, &QTimer::timeout, context, [s](){});
+ QCOMPARE(countedStructObjectsCount, 2);
+ connect(context, &QObject::destroyed, &e, &QEventLoop::quit, Qt::QueuedConnection);
+ context->deleteLater();
+ e.exec();
+ QCOMPARE(countedStructObjectsCount, 1);
+#endif // Q_COMPILER_LAMBDA
+ }
+ QCOMPARE(countedStructObjectsCount, 0);
+}
+
class SubSender : public SenderObject {
Q_OBJECT
};