summaryrefslogtreecommitdiffstats
path: root/tests/auto/corelib
diff options
context:
space:
mode:
authorKent Hansen <kent.hansen@nokia.com>2012-04-20 20:19:12 +0200
committerQt by Nokia <qt-info@nokia.com>2012-05-01 01:18:03 +0200
commit243ea09e4cc0908e28be44e3e6786ff8b73f74be (patch)
tree682dca749a478a3522e2313ea63564692f68ce1b /tests/auto/corelib
parent67245d1708696627a33afa0abc9f3c2b178b8d95 (diff)
Add QMetaMethod-based QObject::connect/disconnectNotify() API
This API will fully replace the const char *-based connectNotify() and disconnectNotify() in Qt5; the old functions will be REMOVED before Qt 5.0 final. The new implementation fixes the long-standing issue of connectNotify() not being called when using the (internal) index-based QMetaObject::connect() (e.g., from QML). As with the old API, there are still two "unintuitive" behaviors concerning disconnectNotify(): - disconnectNotify() is not called when the signal is disconnected using the QObject::disconnect(QMetaObject::Connection) overload. - disconnectNotify() is not called when a receiver is destroyed (i.e., when a connection is implicitly removed). The old versions of connectNotify() and disconnectNotify() are kept for now, and they are still called. They will be removed once known existing reimplementations (e.g., QtNetwork, QtDBus) have been ported to the new API. Change-Id: I8b4f007f3c6d89199c1ba04a3e23c8ca314e0896 Reviewed-by: Olivier Goffart <ogoffart@woboq.com> Reviewed-by: Bradley T. Hughes <bradley.hughes@nokia.com> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com> Reviewed-by: Lars Knoll <lars.knoll@nokia.com> Reviewed-by: Jędrzej Nowacki <jedrzej.nowacki@nokia.com>
Diffstat (limited to 'tests/auto/corelib')
-rw-r--r--tests/auto/corelib/kernel/qobject/tst_qobject.cpp316
1 files changed, 316 insertions, 0 deletions
diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp
index f429af7ce2..fefec5625a 100644
--- a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp
+++ b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp
@@ -77,6 +77,12 @@ private slots:
void connectDisconnectNotify_data();
void connectDisconnectNotify();
void connectNotifyPtr();
+ void connectDisconnectNotifyMethod_data();
+ void connectDisconnectNotifyMethod();
+ void connectDisconnectNotifyMethodPMF();
+ void disconnectNotifyMethod_receiverDestroyed();
+ void connectNotifyMethod_connectSlotsByName();
+ void connectDisconnectNotifyMethod_shadowing();
void emitInDefinedOrder();
void customTypes();
void streamCustomTypes();
@@ -887,6 +893,316 @@ void tst_QObject::connectNotifyPtr()
delete r;
}
+class NotifyMethodObject : public SenderObject, public ReceiverObject
+{
+public:
+ NotifyMethodObject() : SenderObject(), ReceiverObject()
+ {}
+
+ QList<QMetaMethod> connectedSignals;
+ QList<QMetaMethod> disconnectedSignals;
+ void clearNotifications()
+ {
+ connectedSignals.clear();
+ disconnectedSignals.clear();
+ }
+protected:
+ void connectNotify(const QMetaMethod &signal)
+ { connectedSignals.append(signal); }
+ void disconnectNotify(const QMetaMethod &signal)
+ { disconnectedSignals.append(signal); }
+};
+
+void tst_QObject::connectDisconnectNotifyMethod_data()
+{
+ tst_QObject::connectDisconnectNotify_data();
+}
+
+void tst_QObject::connectDisconnectNotifyMethod()
+{
+ NotifyMethodObject *s = new NotifyMethodObject;
+ NotifyMethodObject *r = new NotifyMethodObject;
+
+ QFETCH(QString, a_signal);
+ QFETCH(QString, a_slot);
+
+ // Obtaining meta methods
+ int signalIndx = ((SenderObject*)s)->metaObject()->indexOfSignal(
+ QMetaObject::normalizedSignature(a_signal.toLatin1().constData()+1).constData());
+ int methodIndx = ((ReceiverObject*)r)->metaObject()->indexOfMethod(
+ QMetaObject::normalizedSignature(a_slot.toLatin1().constData()+1).constData());
+ QMetaMethod signal = ((SenderObject*)s)->metaObject()->method(signalIndx);
+ QMetaMethod method = ((ReceiverObject*)r)->metaObject()->method(methodIndx);
+ QVERIFY(signal.isValid());
+ QVERIFY(method.isValid());
+
+ // Test connectNotify
+ QVERIFY(QObject::connect((SenderObject*)s, a_signal.toLatin1(), (ReceiverObject*)r, a_slot.toLatin1()));
+ QCOMPARE(s->connectedSignals.size(), 1);
+ QCOMPARE(s->connectedSignals.at(0), signal);
+ QVERIFY(s->disconnectedSignals.isEmpty());
+
+ // Test disconnectNotify
+ QVERIFY(QObject::disconnect((SenderObject*)s, a_signal.toLatin1(), (ReceiverObject*)r, a_slot.toLatin1()));
+ QCOMPARE(s->disconnectedSignals.size(), 1);
+ QCOMPARE(s->disconnectedSignals.at(0), signal);
+ QCOMPARE(s->connectedSignals.size(), 1);
+
+ // Reconnect
+ s->clearNotifications();
+ QVERIFY(QObject::connect((SenderObject*)s, a_signal.toLatin1(), (ReceiverObject*)r, a_slot.toLatin1()));
+ QCOMPARE(s->connectedSignals.size(), 1);
+ QCOMPARE(s->connectedSignals.at(0), signal);
+ QVERIFY(s->disconnectedSignals.isEmpty());
+
+ // Test disconnectNotify for a complete disconnect
+ QVERIFY(((SenderObject*)s)->disconnect((ReceiverObject*)r));
+ QCOMPARE(s->disconnectedSignals.size(), 1);
+ QCOMPARE(s->disconnectedSignals.at(0), QMetaMethod());
+ QCOMPARE(s->connectedSignals.size(), 1);
+
+ // Test connectNotify when connecting by QMetaMethod
+ s->clearNotifications();
+ QVERIFY(QObject::connect((SenderObject*)s, signal, (ReceiverObject*)r, method));
+ QCOMPARE(s->connectedSignals.size(), 1);
+ QCOMPARE(s->connectedSignals.at(0), signal);
+ QVERIFY(s->disconnectedSignals.isEmpty());
+
+ // Test disconnectNotify when disconnecting by QMetaMethod
+ QVERIFY(QObject::disconnect((SenderObject*)s, signal, (ReceiverObject*)r, method));
+ QCOMPARE(s->disconnectedSignals.size(), 1);
+ QCOMPARE(s->disconnectedSignals.at(0), signal);
+ QCOMPARE(s->connectedSignals.size(), 1);
+
+ // Reconnect
+ s->clearNotifications();
+ QVERIFY(QObject::connect((SenderObject*)s, a_signal.toLatin1(), (ReceiverObject*)r, a_slot.toLatin1()));
+
+ // Test disconnectNotify for a complete disconnect by QMetaMethod
+ QVERIFY(QObject::disconnect((SenderObject*)s, QMetaMethod(), 0, QMetaMethod()));
+ QCOMPARE(s->disconnectedSignals.size(), 1);
+ QCOMPARE(s->disconnectedSignals.at(0), QMetaMethod());
+ QCOMPARE(s->connectedSignals.size(), 1);
+
+ // Test connectNotify when connecting by index
+ s->clearNotifications();
+ QVERIFY(QMetaObject::connect((SenderObject*)s, signalIndx, (ReceiverObject*)r, methodIndx));
+ QCOMPARE(s->connectedSignals.size(), 1);
+ QCOMPARE(s->connectedSignals.at(0), signal);
+ QVERIFY(s->disconnectedSignals.isEmpty());
+
+ // Test disconnectNotify when disconnecting by index
+ QVERIFY(QMetaObject::disconnect((SenderObject*)s, signalIndx, (ReceiverObject*)r, methodIndx));
+ QCOMPARE(s->disconnectedSignals.size(), 1);
+ QCOMPARE(s->disconnectedSignals.at(0), signal);
+ QCOMPARE(s->connectedSignals.size(), 1);
+
+ delete s;
+ delete r;
+}
+
+static void connectDisconnectNotifyTestSlot() {}
+
+void tst_QObject::connectDisconnectNotifyMethodPMF()
+{
+ NotifyMethodObject *s = new NotifyMethodObject;
+ NotifyMethodObject *r = new NotifyMethodObject;
+
+ QMetaMethod signal = QMetaMethod::fromSignal(&SenderObject::signal1);
+
+ // Test connectNotify
+ QVERIFY(QObject::connect((SenderObject*)s, &SenderObject::signal1, (ReceiverObject*)r, &ReceiverObject::slot1));
+ QCOMPARE(s->connectedSignals.size(), 1);
+ QCOMPARE(s->connectedSignals.at(0), signal);
+ QVERIFY(s->disconnectedSignals.isEmpty());
+
+ // Test disconnectNotify
+ QVERIFY(QObject::disconnect((SenderObject*)s, &SenderObject::signal1, (ReceiverObject*)r, &ReceiverObject::slot1));
+ QCOMPARE(s->disconnectedSignals.size(), 1);
+ QCOMPARE(s->disconnectedSignals.at(0), signal);
+ QCOMPARE(s->connectedSignals.size(), 1);
+
+ // Reconnect
+ s->clearNotifications();
+ QVERIFY(QObject::connect((SenderObject*)s, &SenderObject::signal1, (ReceiverObject*)r, &ReceiverObject::slot1));
+ QCOMPARE(s->connectedSignals.size(), 1);
+ QCOMPARE(s->connectedSignals.at(0), signal);
+ QVERIFY(s->disconnectedSignals.isEmpty());
+
+ // Test disconnectNotify with wildcard slot
+ QVERIFY(QObject::disconnect((SenderObject*)s, &SenderObject::signal1, (ReceiverObject*)r, 0));
+ QCOMPARE(s->disconnectedSignals.size(), 1);
+ QCOMPARE(s->disconnectedSignals.at(0), signal);
+ QCOMPARE(s->connectedSignals.size(), 1);
+
+ // Reconnect
+ s->clearNotifications();
+ QMetaObject::Connection conn = connect((SenderObject*)s, &SenderObject::signal1,
+ (ReceiverObject*)r, &ReceiverObject::slot1);
+
+ // Test disconnectNotify when disconnecting by QMetaObject::Connection
+ QVERIFY(QObject::disconnect(conn));
+ // disconnectNotify() is not called, but it probably should be.
+ QVERIFY(s->disconnectedSignals.isEmpty());
+
+ // Test connectNotify when connecting by function pointer
+ s->clearNotifications();
+ QVERIFY(QObject::connect((SenderObject*)s, &SenderObject::signal1, connectDisconnectNotifyTestSlot));
+ QCOMPARE(s->connectedSignals.size(), 1);
+ QCOMPARE(s->connectedSignals.at(0), signal);
+ QVERIFY(s->disconnectedSignals.isEmpty());
+
+ delete s;
+ delete r;
+}
+
+void tst_QObject::disconnectNotifyMethod_receiverDestroyed()
+{
+ NotifyMethodObject *s = new NotifyMethodObject;
+ NotifyMethodObject *r = new NotifyMethodObject;
+
+ QVERIFY(QObject::connect((SenderObject*)s, SIGNAL(signal1()), (ReceiverObject*)r, SLOT(slot1())));
+
+ delete r;
+ // disconnectNotify() is not called, but it probably should be.
+ QVERIFY(s->disconnectedSignals.isEmpty());
+
+ delete s;
+}
+
+class ConnectByNameNotifySenderObject : public QObject
+{
+ Q_OBJECT
+public:
+ QList<QMetaMethod> connectedSignals;
+ QList<QMetaMethod> disconnectedSignals;
+ void clearNotifications()
+ {
+ connectedSignals.clear();
+ disconnectedSignals.clear();
+ }
+protected:
+ void connectNotify(const QMetaMethod &signal)
+ { connectedSignals.append(signal); }
+ void disconnectNotify(const QMetaMethod &signal)
+ { disconnectedSignals.append(signal); }
+Q_SIGNALS:
+ void signal1();
+};
+
+class ConnectByNameNotifyReceiverObject : public QObject
+{
+ Q_OBJECT
+ void createNotifyChild(const char *name)
+ {
+ QObject *o = new ConnectByNameNotifySenderObject;
+ o->setParent(this);
+ o->setObjectName(QString::fromLatin1(name));
+ }
+public:
+ ConnectByNameNotifyReceiverObject()
+ {
+ createNotifyChild("foo");
+ createNotifyChild("bar");
+ createNotifyChild("baz");
+ };
+
+public Q_SLOTS:
+ void on_foo_signal1() {}
+ void on_bar_signal1() {}
+ void on_baz_signal1() {}
+};
+
+void tst_QObject::connectNotifyMethod_connectSlotsByName()
+{
+ ConnectByNameNotifyReceiverObject testObject;
+ QList<ConnectByNameNotifySenderObject *> senders =
+ qFindChildren<ConnectByNameNotifySenderObject *>(&testObject);
+ for (int i = 0; i < senders.size(); ++i) {
+ ConnectByNameNotifySenderObject *o = senders.at(i);
+ QVERIFY(o->connectedSignals.isEmpty());
+ QVERIFY(o->disconnectedSignals.isEmpty());
+ }
+
+ QMetaObject::connectSlotsByName(&testObject);
+
+ for (int i = 0; i < senders.size(); ++i) {
+ ConnectByNameNotifySenderObject *o = senders.at(i);
+ QCOMPARE(o->connectedSignals.size(), 1);
+ QCOMPARE(o->connectedSignals.at(0), QMetaMethod::fromSignal(&ConnectByNameNotifySenderObject::signal1));
+ QVERIFY(o->disconnectedSignals.isEmpty());
+ }
+}
+
+class ConnectDisconnectNotifyShadowObject
+ : public ConnectByNameNotifySenderObject
+{
+ Q_OBJECT
+public Q_SLOTS:
+ void slot1() {}
+Q_SIGNALS:
+ void signal1();
+};
+
+void tst_QObject::connectDisconnectNotifyMethod_shadowing()
+{
+ ConnectDisconnectNotifyShadowObject s;
+ // Obtain QMetaMethods
+ QMetaMethod shadowedSignal1 = QMetaMethod::fromSignal(&ConnectByNameNotifySenderObject::signal1);
+ QMetaMethod redefinedSignal1 = QMetaMethod::fromSignal(&ConnectDisconnectNotifyShadowObject::signal1);
+ QVERIFY(shadowedSignal1 != redefinedSignal1);
+ int slot1Index = s.metaObject()->indexOfSlot("slot1()");
+ QVERIFY(slot1Index != -1);
+ QMetaMethod slot1 = s.metaObject()->method(slot1Index);
+
+ // Test connectNotify
+#ifndef QT_NO_DEBUG
+ const char *warning = "QMetaObject::indexOfSignal: signal signal1() from "
+ "ConnectByNameNotifySenderObject redefined in "
+ "ConnectDisconnectNotifyShadowObject";
+ QTest::ignoreMessage(QtWarningMsg, warning);
+#endif
+ QVERIFY(QObject::connect(&s, SIGNAL(signal1()), &s, SLOT(slot1())));
+ QCOMPARE(s.connectedSignals.size(), 1);
+ QCOMPARE(s.connectedSignals.at(0), redefinedSignal1);
+ QVERIFY(s.disconnectedSignals.isEmpty());
+
+ // Test disconnectNotify
+#ifndef QT_NO_DEBUG
+ QTest::ignoreMessage(QtWarningMsg, warning);
+#endif
+ QVERIFY(QObject::disconnect(&s, SIGNAL(signal1()), &s, SLOT(slot1())));
+ QCOMPARE(s.disconnectedSignals.size(), 1);
+ QCOMPARE(s.disconnectedSignals.at(0), redefinedSignal1);
+ QCOMPARE(s.connectedSignals.size(), 1);
+
+ // Test connectNotify when connecting by shadowed QMetaMethod
+ s.clearNotifications();
+ QVERIFY(QObject::connect(&s, shadowedSignal1, &s, slot1));
+ QCOMPARE(s.connectedSignals.size(), 1);
+ QCOMPARE(s.connectedSignals.at(0), shadowedSignal1);
+ QVERIFY(s.disconnectedSignals.isEmpty());
+
+ // Test disconnectNotify when disconnecting by shadowed QMetaMethod
+ QVERIFY(QObject::disconnect(&s, shadowedSignal1, &s, slot1));
+ QCOMPARE(s.disconnectedSignals.size(), 1);
+ QCOMPARE(s.disconnectedSignals.at(0), shadowedSignal1);
+ QCOMPARE(s.connectedSignals.size(), 1);
+
+ // Test connectNotify when connecting by redefined QMetaMethod
+ s.clearNotifications();
+ QVERIFY(QObject::connect(&s, redefinedSignal1, &s, slot1));
+ QCOMPARE(s.connectedSignals.size(), 1);
+ QCOMPARE(s.connectedSignals.at(0), redefinedSignal1);
+ QVERIFY(s.disconnectedSignals.isEmpty());
+
+ // Test disconnectNotify when disconnecting by redefined QMetaMethod
+ QVERIFY(QObject::disconnect(&s, redefinedSignal1, &s, slot1));
+ QCOMPARE(s.disconnectedSignals.size(), 1);
+ QCOMPARE(s.disconnectedSignals.at(0), redefinedSignal1);
+ QCOMPARE(s.connectedSignals.size(), 1);
+}
+
class SequenceObject : public ReceiverObject
{
Q_OBJECT