From 80532bcd26bd125763fe6b3f9ffd42e535e9b14c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Nordheim?= Date: Fri, 3 Nov 2023 14:06:56 +0100 Subject: Rewrite QuickTests's qWaitForSignal It doesn't use any of the features of QSignalSpy, so it hasn't done it any favors. On top of that QSignalSpy connects with DirectConnection which means that frequently emitted signals on another thread may end up trying to append another signal emission to its internal list during destruction of the signal spy, leading to use-after-free. The new implementation uses a small class with a slot that just sets a boolean. We use AutoConnection so it is thread safe, and pending emissions go away when the object is destroyed. Fixes: QTBUG-118744 Fixes: QTBUG-118163 Pick-to: 6.6 6.5 Change-Id: Ib2ccce2369a681bfe9a2e04d2c091f9690edee49 Reviewed-by: Dimitrios Apostolou Reviewed-by: Ulf Hermann --- src/qmltest/quicktest.cpp | 48 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 12 deletions(-) (limited to 'src/qmltest') diff --git a/src/qmltest/quicktest.cpp b/src/qmltest/quicktest.cpp index c333f7fe1b..b4bfd94d17 100644 --- a/src/qmltest/quicktest.cpp +++ b/src/qmltest/quicktest.cpp @@ -223,22 +223,45 @@ static void handleCompileErrors( results.stopLogging(); } +class SimpleReceiver : public QObject { + Q_OBJECT +public: + bool signalReceived = false; +public slots: + void slotFun() { signalReceived = true; } +}; + bool qWaitForSignal(QObject *obj, const char* signal, int timeout) { - QSignalSpy spy(obj, signal); - QElapsedTimer timer; - timer.start(); - - while (!spy.size()) { - int remaining = timeout - int(timer.elapsed()); - if (remaining <= 0) - break; - QCoreApplication::processEvents(QEventLoop::AllEvents, remaining); - QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); - QTest::qSleep(10); + if (!obj || !signal) { + qWarning("qWaitForSignal: invalid arguments"); + return false; + } + if (((signal[0] - '0') & 0x03) != QSIGNAL_CODE) { + qWarning("qWaitForSignal: not a valid signal, use the SIGNAL macro"); + return false; + } + + int sig = obj->metaObject()->indexOfSignal(signal + 1); + if (sig == -1) { + const QByteArray ba = QMetaObject::normalizedSignature(signal + 1); + sig = obj->metaObject()->indexOfSignal(ba.constData()); + if (sig == -1) { + qWarning("qWaitForSignal: no such signal %s::%s", obj->metaObject()->className(), + signal); + return false; + } + } + + SimpleReceiver receiver; + static int slot = receiver.metaObject()->indexOfSlot("slotFun()"); + if (!QMetaObject::connect(obj, sig, &receiver, slot)) { + qWarning("qWaitForSignal: failed to connect to signal %s::%s", + obj->metaObject()->className(), signal); + return false; } - return spy.size(); + return QTest::qWaitFor([&]() { return receiver.signalReceived; }, timeout); } template @@ -667,3 +690,4 @@ int quick_test_main_with_setup(int argc, char **argv, const char *name, const ch QT_END_NAMESPACE #include "moc_quicktest_p.cpp" +#include "quicktest.moc" -- cgit v1.2.3