Introduce QSignalSpy constructor allows to spy on a meta method

This functionality is especially convenient if meta-object system is
heavily used in a test. For example, if you need to test a bunch of
signals based on their names and/or argument types.

Change-Id: I09a4ecbbd3d0859b5fd466d9dde7679804eb7614
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
bb10
Vitaly Fanaskov 2019-08-12 14:36:06 +02:00
parent c29a136804
commit e8c7124768
4 changed files with 132 additions and 0 deletions

View File

@ -98,3 +98,51 @@ QVERIFY(spy.wait(1000));
QSignalSpy spy(myPushButton, &QPushButton::clicked);
//! [6]
//! [7]
QObject object;
auto mo = object.metaObject();
auto signalIndex = mo->indexOfSignal("objectNameChanged(QString)");
auto signal = mo->method(signalIndex);
QSignalSpy spy(&object, signal);
object.setObjectName("A new object name");
QCOMPARE(spy.count(), 1);
//! [7]
//! [8]
void tst_QWindow::writeMinMaxDimensionalProps_data()
QTest::addColumn<int>("propertyIndex");
// Collect all relevant properties
static const auto mo = QWindow::staticMetaObject;
for (int i = mo.propertyOffset(); i < mo.propertyCount(); ++i) {
auto property = mo.property(i);
// ...that have type int
if (property.type() == QVariant::Int) {
static const QRegularExpression re("^minimum|maximum");
const auto name = property.name();
// ...and start with "minimum" or "maximum"
if (re.match(name).hasMatch()) {
QTest::addRow("%s", name) << i;
}
}
}
}
void tst_QWindow::writeMinMaxDimensionalProps()
{
QFETCH(int, propertyIndex);
auto property = QWindow::staticMetaObject.property(propertyIndex);
QVERIFY(property.isWritable());
QVERIFY(property.hasNotifySignal());
QWindow window;
QSignalSpy spy(&window, property.notifySignal());
QVERIFY(property.write(&window, 42));
QCOMPARE(spy.count(), 1);
}
//! [8]

View File

@ -118,6 +118,16 @@ public:
}
#endif // Q_CLANG_QDOC
QSignalSpy(const QObject *obj, const QMetaMethod &signal)
: m_waiting(false)
{
if (isObjectValid(obj) && isSignalMetaMethodValid(signal) &&
connectToSignal(obj, signal.methodIndex())) {
sig = signal.methodSignature();
initArgs(signal, obj);
}
}
inline bool isValid() const { return !sig.isEmpty(); }
inline QByteArray signal() const { return sig; }

View File

@ -86,6 +86,28 @@
\snippet code/doc_src_qsignalspy.cpp 6
*/
/*! \fn QSignalSpy(const QObject *obj, const QMetaMethod &signal)
\since 5.14
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 \nullptr 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.
This constructor is convenient to use when Qt's meta-object system is
heavily used in a test.
Basic usage example:
\snippet code/doc_src_qsignalspy.cpp 7
Imagine we need to check whether all properties of the QWindow class
that represent minimum and maximum dimensions are properly writable.
The following example demonstrates one of the approaches:
\snippet code/doc_src_qsignalspy.cpp 8
*/
/*! \fn QSignalSpy::isValid() const
Returns \c true if the signal spy listens to a valid signal, otherwise false.

View File

@ -65,6 +65,11 @@ private slots:
void waitFunctionPointer_signalEmittedLater();
void waitFunctionPointer_signalEmittedTooLate();
void waitFunctionPointer_signalEmittedMultipleTimes();
void spyOnMetaMethod();
void spyOnMetaMethod_invalid();
void spyOnMetaMethod_invalid_data();
};
class QtTestObject: public QObject
@ -432,5 +437,52 @@ void tst_QSignalSpy::waitFunctionPointer_signalEmittedMultipleTimes()
QCOMPARE(spy.count(), 3);
}
void tst_QSignalSpy::spyOnMetaMethod()
{
QObject obj;
auto mo = obj.metaObject();
auto signalIndex = mo->indexOfSignal("objectNameChanged(QString)");
QVERIFY(signalIndex != -1);
auto signal = mo->method(signalIndex);
QVERIFY(signal.isValid());
QCOMPARE(signal.methodType(), QMetaMethod::Signal);
QSignalSpy spy(&obj, signal);
QVERIFY(spy.isValid());
obj.setObjectName("A new object name");
QCOMPARE(spy.count(), 1);
}
Q_DECLARE_METATYPE(QMetaMethod);
void tst_QSignalSpy::spyOnMetaMethod_invalid()
{
QFETCH(QObject*, object);
QFETCH(QMetaMethod, signal);
QSignalSpy spy(object, signal);
QVERIFY(!spy.isValid());
}
void tst_QSignalSpy::spyOnMetaMethod_invalid_data()
{
QTest::addColumn<QObject*>("object");
QTest::addColumn<QMetaMethod>("signal");
QTest::addRow("Invalid object")
<< static_cast<QObject*>(nullptr)
<< QMetaMethod();
QTest::addRow("Empty signal")
<< new QObject(this)
<< QMetaMethod();
QTest::addRow("Method is not a signal")
<< new QObject(this)
<< QObject::staticMetaObject.method(QObject::staticMetaObject.indexOfMethod("deleteLater()"));
}
QTEST_MAIN(tst_QSignalSpy)
#include "tst_qsignalspy.moc"