diff options
author | MÃ¥rten Nordheim <marten.nordheim@qt.io> | 2023-11-03 14:06:56 +0100 |
---|---|---|
committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2023-11-06 19:46:14 +0000 |
commit | 5b111a8d4be35105eebfd32dfaca2da890502f3e (patch) | |
tree | 4412c5dce4001986de6305eeaf91af02257ac839 | |
parent | f95f24467b553c406f58a555f154c97f76df1eac (diff) |
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.5
Change-Id: Ib2ccce2369a681bfe9a2e04d2c091f9690edee49
Reviewed-by: Dimitrios Apostolou <jimis@qt.io>
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
(cherry picked from commit 80532bcd26bd125763fe6b3f9ffd42e535e9b14c)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r-- | src/qmltest/quicktest.cpp | 48 |
1 files changed, 36 insertions, 12 deletions
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 <typename... Args> @@ -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" |