aboutsummaryrefslogtreecommitdiffstats
path: root/src/qmltest
diff options
context:
space:
mode:
authorMårten Nordheim <marten.nordheim@qt.io>2023-11-03 14:06:56 +0100
committerMårten Nordheim <marten.nordheim@qt.io>2023-11-06 15:05:30 +0000
commit80532bcd26bd125763fe6b3f9ffd42e535e9b14c (patch)
tree67494ea45e4c159d44423392144ee3074978b464 /src/qmltest
parentf2bb22b541b49e2a899062051f0774cc1151b66b (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.6 6.5 Change-Id: Ib2ccce2369a681bfe9a2e04d2c091f9690edee49 Reviewed-by: Dimitrios Apostolou <jimis@qt.io> Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Diffstat (limited to 'src/qmltest')
-rw-r--r--src/qmltest/quicktest.cpp48
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"