QSignalSpy: Added template function pointer constructor

Added the function pointer constructor to QSignalSpy to take advantage
of the new connect syntax.

Change-Id: I94218a096c677cdba73e1b2cfa8b9c09bc28145f
Reviewed-by: Olivier Goffart <ogoffart@woboq.com>
Reviewed-by: Jason McDonald <macadder1@gmail.com>
This commit is contained in:
Keith Gardner 2014-04-16 20:43:56 -05:00 committed by The Qt Project
parent eb88aaa3a2
commit 6fc7d76e73
4 changed files with 229 additions and 0 deletions

View File

@ -83,3 +83,8 @@ QSignalSpy spy(myPushButton, SIGNAL(clicked(bool)));
//! [5]
QVERIFY(spy.wait(1000));
//! [5]
//! [6]
QSignalSpy spy(myPushButton, &QPushButton::clicked);
//! [6]

View File

@ -98,6 +98,48 @@ public:
initArgs(mo->method(sigIndex), obj);
}
#ifdef Q_QDOC
QSignalSpy(const QObject *object, PointerToMemberFunction signal);
#else
template <typename Func>
QSignalSpy(const typename QtPrivate::FunctionPointer<Func>::Object *obj, Func signal0)
: m_waiting(false)
{
#ifdef Q_CC_BOR
const int memberOffset = QObject::staticMetaObject.methodCount();
#else
static const int memberOffset = QObject::staticMetaObject.methodCount();
#endif
if (!obj) {
qWarning("QSignalSpy: Cannot spy on a null object");
return;
}
if (!signal0) {
qWarning("QSignalSpy: Null signal name is not valid");
return;
}
const QMetaObject * const mo = obj->metaObject();
const QMetaMethod signalMetaMethod = QMetaMethod::fromSignal(signal0);
const int sigIndex = signalMetaMethod.methodIndex();
if (!signalMetaMethod.isValid() ||
signalMetaMethod.methodType() != QMetaMethod::Signal) {
qWarning("QSignalSpy: Not a valid signal: '%s'",
signalMetaMethod.methodSignature().constData());
return;
}
if (!QMetaObject::connect(obj, sigIndex, this, memberOffset,
Qt::DirectConnection, 0)) {
qWarning("QSignalSpy: QMetaObject::connect returned false. Unable to connect.");
return;
}
sig = signalMetaMethod.methodSignature();
initArgs(mo->method(sigIndex), obj);
}
#endif // Q_QDOC
inline bool isValid() const { return !sig.isEmpty(); }
inline QByteArray signal() const { return sig; }

View File

@ -72,6 +72,20 @@
\snippet code/doc_src_qsignalspy.cpp 4
*/
/*! \fn QSignalSpy::QSignalSpy(const QObject *object, PointerToMemberFunction signal)
\since 5.4
Constructs a new QSignalSpy that listens for emissions of the \a signal
from the QObject \a object. If QSignalSpy is not able to listen for a
valid signal (for example, because \a object is null or \a signal does
not denote a valid signal of \a object), an explanatory warning message
will be output using qWarning() and subsequent calls to \c isValid() will
return false.
Example:
\snippet code/doc_src_qsignalspy.cpp 6
*/
/*! \fn QSignalSpy::isValid() const
Returns \c true if the signal spy listens to a valid signal, otherwise false.

View File

@ -65,6 +65,19 @@ private slots:
void wait_signalEmittedLater();
void wait_signalEmittedTooLate();
void wait_signalEmittedMultipleTimes();
void spyFunctionPointerWithoutArgs();
void spyFunctionPointerWithBasicArgs();
void spyFunctionPointerWithPointers();
void spyFunctionPointerWithQtClasses();
void spyFunctionPointerWithBasicQtClasses();
void spyFunctionPointerWithQtTypedefs();
void waitFunctionPointer_signalEmitted();
void waitFunctionPointer_timeout();
void waitFunctionPointer_signalEmittedLater();
void waitFunctionPointer_signalEmittedTooLate();
void waitFunctionPointer_signalEmittedMultipleTimes();
};
class QtTestObject: public QObject
@ -278,5 +291,160 @@ void tst_QSignalSpy::wait_signalEmittedMultipleTimes()
QCOMPARE(spy.count(), 3);
}
void tst_QSignalSpy::spyFunctionPointerWithoutArgs()
{
QtTestObject obj;
QSignalSpy spy(&obj, &QtTestObject::sig0);
QCOMPARE(spy.count(), 0);
emit obj.sig0();
QCOMPARE(spy.count(), 1);
emit obj.sig0();
QCOMPARE(spy.count(), 2);
QList<QVariant> args = spy.takeFirst();
QCOMPARE(args.count(), 0);
}
void tst_QSignalSpy::spyFunctionPointerWithBasicArgs()
{
QtTestObject obj;
QSignalSpy spy(&obj, &QtTestObject::sig1);
emit obj.sig1(1, 2);
QCOMPARE(spy.count(), 1);
QList<QVariant> args = spy.takeFirst();
QCOMPARE(args.count(), 2);
QCOMPARE(args.at(0).toInt(), 1);
QCOMPARE(args.at(1).toInt(), 2);
QSignalSpy spyl(&obj, &QtTestObject::sigLong);
emit obj.sigLong(1l, 2l);
args = spyl.takeFirst();
QCOMPARE(args.count(), 2);
QCOMPARE(qvariant_cast<long>(args.at(0)), 1l);
QCOMPARE(qvariant_cast<long>(args.at(1)), 2l);
}
void tst_QSignalSpy::spyFunctionPointerWithPointers()
{
qRegisterMetaType<int *>("int*");
QtTestObject obj;
QSignalSpy spy(&obj, &QtTestObject::sig2);
int i1 = 1;
int i2 = 2;
emit obj.sig2(&i1, &i2);
QCOMPARE(spy.count(), 1);
QList<QVariant> args = spy.takeFirst();
QCOMPARE(args.count(), 2);
QCOMPARE(*static_cast<int * const *>(args.at(0).constData()), &i1);
QCOMPARE(*static_cast<int * const *>(args.at(1).constData()), &i2);
}
void tst_QSignalSpy::spyFunctionPointerWithBasicQtClasses()
{
QtTestObject2 obj;
QSignalSpy spy(&obj, &QtTestObject2::sig);
emit obj.sig(QString("bubu"));
QCOMPARE(spy.count(), 1);
QCOMPARE(spy.at(0).count(), 1);
QCOMPARE(spy.at(0).at(0).toString(), QString("bubu"));
QSignalSpy spy2(&obj, &QtTestObject2::sig5);
QVariant val(45);
emit obj.sig5(val);
QCOMPARE(spy2.count(), 1);
QCOMPARE(spy2.at(0).count(), 1);
QCOMPARE(spy2.at(0).at(0), val);
QCOMPARE(qvariant_cast<QVariant>(spy2.at(0).at(0)), val);
}
void tst_QSignalSpy::spyFunctionPointerWithQtClasses()
{
QtTestObject2 obj;
QSignalSpy spy(&obj, &QtTestObject2::sig2);
QDateTime dt = QDateTime::currentDateTime();
emit obj.sig2(dt);
QCOMPARE(spy.count(), 1);
QCOMPARE(spy.at(0).count(), 1);
QCOMPARE(spy.at(0).at(0).typeName(), "QDateTime");
QCOMPARE(*static_cast<const QDateTime *>(spy.at(0).at(0).constData()), dt);
QCOMPARE(spy.at(0).at(0).toDateTime(), dt);
QSignalSpy spy2(&obj, &QtTestObject2::sig3);
emit obj.sig3(this);
QCOMPARE(*static_cast<QObject * const *>(spy2.value(0).value(0).constData()),
(QObject *)this);
QCOMPARE(qvariant_cast<QObject *>(spy2.value(0).value(0)), (QObject*)this);
QSignalSpy spy3(&obj, &QtTestObject2::sig4);
emit obj.sig4(QChar('A'));
QCOMPARE(qvariant_cast<QChar>(spy3.value(0).value(0)), QChar('A'));
}
void tst_QSignalSpy::spyFunctionPointerWithQtTypedefs()
{
QtTestObject3 obj;
QSignalSpy spy2(&obj, &QtTestObject3::sig2);
emit obj.sig2(42, 43);
QCOMPARE(spy2.value(0).value(0).toInt(), 42);
QCOMPARE(spy2.value(0).value(1).toInt(), 43);
}
void tst_QSignalSpy::waitFunctionPointer_signalEmitted()
{
QTimer::singleShot(0, this, SIGNAL(sigFoo()));
QSignalSpy spy(this, &tst_QSignalSpy::sigFoo);
QVERIFY(spy.wait(1));
}
void tst_QSignalSpy::waitFunctionPointer_timeout()
{
QSignalSpy spy(this, &tst_QSignalSpy::sigFoo);
QVERIFY(!spy.wait(1));
}
void tst_QSignalSpy::waitFunctionPointer_signalEmittedLater()
{
QTimer::singleShot(500, this, SIGNAL(sigFoo()));
QSignalSpy spy(this, &tst_QSignalSpy::sigFoo);
QVERIFY(spy.wait(1000));
}
void tst_QSignalSpy::waitFunctionPointer_signalEmittedTooLate()
{
QTimer::singleShot(500, this, SIGNAL(sigFoo()));
QSignalSpy spy(this, &tst_QSignalSpy::sigFoo);
QVERIFY(!spy.wait(200));
QTest::qWait(400);
QCOMPARE(spy.count(), 1);
}
void tst_QSignalSpy::waitFunctionPointer_signalEmittedMultipleTimes()
{
QTimer::singleShot(100, this, SIGNAL(sigFoo()));
QTimer::singleShot(800, this, SIGNAL(sigFoo()));
QSignalSpy spy(this, &tst_QSignalSpy::sigFoo);
QVERIFY(spy.wait());
QCOMPARE(spy.count(), 1); // we don't wait for the second signal...
QVERIFY(spy.wait());
QCOMPARE(spy.count(), 2);
QVERIFY(!spy.wait(1));
QTimer::singleShot(10, this, SIGNAL(sigFoo()));
QVERIFY(spy.wait());
QCOMPARE(spy.count(), 3);
}
QTEST_MAIN(tst_QSignalSpy)
#include "tst_qsignalspy.moc"